Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/chatbot error handling #49

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

Rahulsoni9321
Copy link

@Rahulsoni9321 Rahulsoni9321 commented Dec 6, 2024

Summary by CodeRabbit

  • New Features
    • Introduced an interactive AI summarization feature for blog posts with a redesigned chat interface.
    • Added animated chat suggestions and real-time toast notifications for improved user feedback.
  • Chores
    • Updated configuration settings and added new dependencies, including various libraries for enhanced functionality.
    • Enhanced UI styling with fresh animations, improved scrollbars, and refined section designs.
  • Refactor
    • Overhauled the AI content generation endpoint with improved error handling and validation.
  • Documentation
    • Enriched blog post metadata with detailed descriptions for better clarity and context.

Copy link

vercel bot commented Dec 6, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
devtoolsacademy ❌ Failed (Inspect) Dec 6, 2024 8:42pm

Copy link

vercel bot commented Dec 6, 2024

@Rahulsoni9321 is attempting to deploy a commit to the Ankur 's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Dec 6, 2024

Walkthrough

This update introduces several enhancements and new features across the project. Environment configuration has been updated with new variables and dependencies, while the API endpoint has been refactored to use Google Generative AI with improved input validation and error handling. Additionally, new UI components and styling rules have been added, including a comprehensive toast notification system, blog components with enhanced metadata and chat functionality, and layout adjustments. Minor formatting tweaks and further improvement of the Tailwind CSS configuration round out the changes.

Changes

File(s) Change Summary
.env.example, .vscode/settings.json Added new environment variables (GEMINI_API_KEY, NEXT_PUBLIC_UMAMI_WEBSITE_ID) and updated editor configuration for TypeScript React.
package.json Introduced multiple new dependencies (e.g., @google/generative-ai, @heroicons/react, framer-motion, etc.) without altering existing ones.
src/app/api/summarize/route.ts Replaced Anthropic API integration with Google Generative AI by validating GEMINI_API_KEY, updating prompt construction, and refining error handling.
src/app/globals.css, tailwind.config.ts Added new CSS rules for .section-divider and scrollbar styles; extended Tailwind configuration with animations, color themes, typography, and border radius.
src/app/layout.tsx Integrated the Toaster component into the layout by restructuring with a flex container that includes Navbar, main, and Footer.
src/components/ui/toast.tsx, src/components/ui/toaster.tsx, src/hooks/use-toast.ts Implemented a comprehensive toast notification system with new providers, components, custom hook, and state management improvements.
src/components/BlogPostClient.tsx, src/components/ChatSuggestions.tsx, src/components/blog/BlogChatInterface.tsx, src/components/blog/BlogHeader.tsx, MDX files under src/app/blog/*/page.mdx Added new blog components and properties (including a description prop) to enhance metadata, integrate a chat interface with summary functionality, and update call-to-action labels.
src/components/blog/CommentSection.tsx Made minor formatting adjustments for consistent code indentation.

Suggested labels

enhancement, performance, UX, accessibility, technical, ui

Suggested reviewers

  • tyaga001

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm warn config production Use --omit=dev instead.
npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: next-themes@0.3.0
npm error Found: react@19.0.0
npm error node_modules/react
npm error dev react@"^19.0.0" from the root project
npm error peer react@"^18.0.0 || ^19.0.0 || ^19.0.0-0" from @clerk/clerk-react@5.24.1
npm error node_modules/@clerk/clerk-react
npm error @clerk/clerk-react@"^5.24.1" from @clerk/nextjs@6.12.2
npm error node_modules/@clerk/nextjs
npm error dev @clerk/nextjs@"^6.12.0" from the root project
npm error 54 more (@clerk/nextjs, @clerk/shared, @floating-ui/react-dom, ...)
npm error
npm error Could not resolve dependency:
npm error peer react@"^16.8 || ^17 || ^18" from next-themes@0.3.0
npm error node_modules/next-themes
npm error dev next-themes@"^0.3.0" from the root project
npm error
npm error Conflicting peer dependency: react@18.3.1
npm error node_modules/react
npm error peer react@"^16.8 || ^17 || ^18" from next-themes@0.3.0
npm error node_modules/next-themes
npm error dev next-themes@"^0.3.0" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /.npm/_logs/2025-03-02T18_36_48_426Z-eresolve-report.txt
npm error A complete log of this run can be found in: /.npm/_logs/2025-03-02T18_36_48_426Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 10

🧹 Outside diff range and nitpick comments (12)
src/app/api/summarize/route.ts (1)

9-11: Provide a more specific error message when API key is missing

The error message "Something went wrong while fetching API Keys." is too generic. It would be better to specify which API key is missing to aid in debugging.

Apply this diff to improve the error message:

if (!GEMINI_API_KEY) {
-  throw new Error("Something went wrong while fetching API Keys.")
+  throw new Error("GEMINI_API_KEY environment variable is not set.")
}
src/hooks/use-toast.ts (2)

12-12: Toast removal delay is unusually long

The TOAST_REMOVE_DELAY is set to 1000000 milliseconds (approximately 16.67 minutes), which may be longer than intended for a toast notification. Consider reducing it to a more typical duration, such as 3000 milliseconds (3 seconds).

Apply this diff to adjust the toast removal delay:

-const TOAST_REMOVE_DELAY = 1000000
+const TOAST_REMOVE_DELAY = 3000

11-11: Consider increasing the toast limit for better user experience

The TOAST_LIMIT is set to 1, meaning only one toast notification can be displayed at a time. If multiple notifications are expected or can occur simultaneously, consider increasing this limit to improve user experience.

Adjust the toast limit as needed:

-const TOAST_LIMIT = 1
+const TOAST_LIMIT = 3 // or another suitable number
src/components/BlogChatInterface.tsx (1)

79-82: Remove unnecessary 'async' keyword from 'handleSummary' function

The handleSummary function is declared as async but does not use await, making the async keyword unnecessary.

Apply this diff to remove the async keyword:

-const handleSummary = async () => {
+const handleSummary = () => {
    handleSend("Please Summarize this blog for me.")
}
src/app/globals.css (1)

92-102: Clean up scrollbar styling

The scrollbar styling has some minor formatting issues:

Apply these changes:

 .ChatArea::-webkit-scrollbar {
-  width: 6px ;
+  width: 6px;
   border-radius: 4px;
   background-color: #6e6b6b;
 }
 
 .ChatArea::-webkit-scrollbar-thumb {
-  
   background-color: white;
   border-radius: 4px;
 }
src/components/ChatSuggestions.tsx (2)

7-7: Improve type safety for handleSend

The handleSend function type should specify the expected string format for better type safety.

Consider this change:

-    handleSend: (customPrompt?: string) => void
+    handleSend: (customPrompt?: `Make me understand this blog : ${string}` | `What does "${string}" means in this blog? `) => void

11-15: Remove unnecessary empty lines

Multiple consecutive empty lines should be removed to maintain code cleanliness.

package.json (2)

27-27: Consider consolidating toast libraries

You've added both @radix-ui/react-toast and sonner which serve similar purposes. Consider using just one to reduce bundle size and maintain consistency.

Also applies to: 53-53


16-16: Consider pinning dependency versions for security

For better security and reproducibility, consider pinning versions (using exact versions instead of ^) for the new dependencies:

  • @google/generative-ai
  • @types/dompurify
  • dompurify
  • html-react-parser

This helps prevent potential security issues from automatic minor version updates.

Also applies to: 28-28, 35-35, 40-40

src/components/BlogPostClient.tsx (1)

51-54: Consider adding loading state to the button

For better UX during AI summarization, consider adding a loading state to the button.

-    <button onClick={()=>setShowChat(true)} className="inline-flex h-10 animate-shimmer items-center justify-center  border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-6 font-medium text-slate-200 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50 rounded-full text-sm ">
-    Summarize with AI
-    </button>
+    <button 
+      onClick={()=>setShowChat(true)} 
+      disabled={isLoading}
+      className="inline-flex h-10 animate-shimmer items-center justify-center border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-6 font-medium text-slate-200 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50 rounded-full text-sm disabled:opacity-50"
+    >
+      {isLoading ? (
+        <>
+          <span className="mr-2">Summarizing...</span>
+          <span className="animate-spin">⚡</span>
+        </>
+      ) : (
+        'Summarize with AI'
+      )}
+    </button>
tailwind.config.ts (2)

21-23: Remove unnecessary empty lines

There are multiple consecutive empty lines that should be removed to maintain code cleanliness.


Line range hint 1-1: Consider restructuring PR scope

The current changes appear to mix styling updates with error handling functionality. Consider:

  1. Separating pure styling changes into a different PR
  2. Including documentation on how these UI changes support error handling
  3. Adding error-specific styling variations (colors, animations) if these are meant for error states
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 79522c9 and 4b2d930.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (18)
  • .env.example (1 hunks)
  • .vscode/settings.json (1 hunks)
  • components.json (1 hunks)
  • package.json (3 hunks)
  • src/app/api/summarize/route.ts (1 hunks)
  • src/app/blog/[slug]/page.tsx (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/components/BlogChatInterface.tsx (3 hunks)
  • src/components/BlogPostClient.tsx (2 hunks)
  • src/components/ChatSuggestions.tsx (1 hunks)
  • src/components/Hero.tsx (1 hunks)
  • src/components/MDXComponents.tsx (1 hunks)
  • src/components/Navbar.tsx (1 hunks)
  • src/components/ui/toast.tsx (1 hunks)
  • src/components/ui/toaster.tsx (1 hunks)
  • src/hooks/use-toast.ts (1 hunks)
  • tailwind.config.ts (3 hunks)
✅ Files skipped from review due to trivial changes (4)
  • .vscode/settings.json
  • .env.example
  • src/components/Navbar.tsx
  • src/components/MDXComponents.tsx
🔇 Additional comments (9)
src/app/blog/[slug]/page.tsx (1)

29-29: Verify PR scope: Image alt text vs chatbot error handling

This change appears unrelated to the PR's objective of implementing chatbot error handling. Please clarify if this accessibility improvement should be in a separate PR for better traceability.

src/app/api/summarize/route.ts (1)

36-38: Verify the method to access the AI response content

The current code uses result.response.text() to access the generated content. Please verify if result.response.text() is the correct method. Depending on the API, you may need to access result.response directly or another property.

Run the following script to check the properties of the result object:

✅ Verification successful

Let me gather more information about the API usage and response structure.


Let me check the package dependencies and documentation references.


The code is using the correct method to access the AI response content

Based on the Google Generative AI SDK version (^0.21.0) being used in the project, result.response.text() is the correct method to access the generated content from the model's response. This is consistent with the official Google Generative AI SDK's API for accessing text content from the response object.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the properties of the 'result' object returned by 'generateContent' method.

# Since we cannot execute the code, check for documentation or type definitions.
rg -A 5 -B 2 'generateContent' src/

# Search for 'GoogleGenerativeAI' usage to find examples.
rg 'GoogleGenerativeAI' src/

Length of output: 948


Script:

#!/bin/bash
# Search for type definitions or examples of response handling
rg -A 3 "response.text" src/

# Look for any documentation or type information in the codebase
ast-grep --pattern 'interface GenerateContentResponse {
  $$$
}'

# Search for other instances of result handling
rg -A 5 "model.generateContent" src/

Length of output: 673


Script:

#!/bin/bash
# Check package.json for @google/generative-ai version
cat package.json | grep "@google/generative-ai"

# Look for any type imports or references
rg "from '@google/generative-ai'" src/
rg "from \"@google/generative-ai\"" src/

# Search for any comments or documentation about response handling
rg -B 2 -A 2 "model.generateContent" --type ts --type js src/

Length of output: 712

components.json (1)

1-21: Configuration file looks good

The components.json file is well-structured and correctly defines the UI component configuration.

src/app/layout.tsx (2)

12-12: LGTM: Toast implementation looks good

The Toaster component is correctly placed inside ClerkProvider for global availability of toast notifications.

Also applies to: 86-86


73-74: 🛠️ Refactor suggestion

Consider moving sensitive data to environment variables

The Umami website ID should be accessed via environment variables for better security.

-    data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID}
+    data-website-id={process.env.NEXT_PUBLIC_UMAMI_WEBSITE_ID ?? ''}

Likely invalid or redundant comment.

src/components/BlogPostClient.tsx (2)

21-28: LGTM: Props destructuring improves readability

The vertical alignment of props destructuring enhances code readability.


64-66: Verify BlogChatInterface prop types

New props have been added to BlogChatInterface. Let's verify their types are properly defined.

✅ Verification successful

Props are correctly defined and match the usage

The interface BlogChatInterfaceProps properly defines all the props being passed in the code snippet, including blogContent, blogTitle, and blogDescription, all typed as strings. The implementation is type-safe and consistent.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check BlogChatInterface component for proper prop types
ast-grep --pattern 'interface BlogChatInterfaceProps {
  $$$
  blogDescription: string;
  blogTitle: string;
  $$$
}'

Length of output: 119


Script:

#!/bin/bash
# Let's first find the BlogChatInterface component file
fd "BlogChatInterface" --type f

# Then let's check for interface or type definitions
rg "interface.*BlogChatInterface.*Props|type.*BlogChatInterface.*Props" -A 10

Length of output: 979

src/components/Hero.tsx (1)

11-11: Style changes appear unrelated to chatbot error handling

The modifications to the Hero component seem to be purely styling-related (padding and dimension changes) and don't align with the PR's stated objective of implementing chatbot error handling. Consider:

  1. Moving these styling changes to a separate PR
  2. Explaining how these changes relate to error handling if they are indeed necessary

Let's verify if these style changes are referenced in any error handling related files:

Also applies to: 17-17

tailwind.config.ts (1)

57-64: Verify shimmer animation usage in error states

The shimmer animation addition could be useful for loading/error states in the chatbot UI. However, ensure that:

  1. The animation timing (2s) is appropriate for error state feedback
  2. Consider adding error-specific variations (e.g., red shimmer for errors)

Let's check where this animation is being used:

Also applies to: 104-104

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: 1

🧹 Outside diff range and nitpick comments (10)
src/app/api/summarize/route.ts (4)

9-11: Improve error message for missing API key

The current error message is generic. Consider providing a more specific message that helps developers identify and fix the issue quickly.

if (!GEMINI_API_KEY) {
-  throw new Error("Something went wrong while fetching API Keys.")
+  throw new Error("GEMINI_API_KEY environment variable is not configured. Please set it in your environment variables.")
}

14-16: Consider extracting system instructions to a separate constant

The system instructions are embedded directly in the model configuration. Consider extracting them to a separate constant for better maintainability and readability.

+const SYSTEM_INSTRUCTIONS = `You're an AI assistant, Who only answers questions related to the blog post that have provided to you in the prompt.

+Try to Answer using numeric points for readability and Bold the title of the point.

+If the user wants help related to the topic mentioned in the blog you can answer that.

+If the question is not related to the blog post or the topic that is mentioned on the blog, Throw error response.`;

const model = genAI.getGenerativeModel({
  model: "gemini-1.5-flash",
-  systemInstruction: "You're an AI assistant, Who only answers questions related to the blog post that have provided to you in the prompt.\n\nTry to Answer using numeric points for readability and Bold the title of the point. \n\n If the user wants help related to the topic mentioned in the blog you can answer that.\n\n If the question is not related to the blog post or the topic that is mentioned on the blog, Throw error response."
+  systemInstruction: SYSTEM_INSTRUCTIONS
});

23-28: Enhance validation error messages

The current validation returns a generic error message. Consider providing more specific feedback about which field failed validation.

-  if (!body.content || typeof body.content !== 'string' || !body.query || typeof body.query !== 'string') {
+  const errors = [];
+  if (!body.content || typeof body.content !== 'string') {
+    errors.push('content must be a non-empty string');
+  }
+  if (!body.query || typeof body.query !== 'string') {
+    errors.push('query must be a non-empty string');
+  }
+  if (errors.length > 0) {
     return NextResponse.json(
-      { error: 'Invalid request: content and query are required and must be strings', success: false },
+      { errors },
       { status: 400 }
     );
   }

43-47: Enhance error logging structure

Consider using a structured logging format for better error tracking and analysis.

-    console.error('API Error:', {
-      type: isGoogleAIError ? 'GoogleGenerativeAI' : 'Unknown',
-      message: error.message,
-      timestamp: new Date().toISOString()
-    });
+    console.error(JSON.stringify({
+      level: 'error',
+      service: 'summarize-api',
+      errorType: isGoogleAIError ? 'GoogleGenerativeAI' : 'Unknown',
+      message: error.message,
+      timestamp: new Date().toISOString(),
+      stack: error.stack
+    }));
src/components/BlogChatInterface.tsx (3)

62-67: Consider adding retry mechanism for failed requests

For transient errors, implementing a retry mechanism could improve reliability.

+    const MAX_RETRIES = 3;
+    const RETRY_DELAY = 1000; // ms
+    
+    const fetchWithRetry = async (retries = 0) => {
+      try {
         const response = await fetch('/api/summarize', {
           method: 'POST',
           headers: { 'Content-Type': 'application/json' },
           body: JSON.stringify({ content: blogContent, query: promptToSend }),
         });
         
         if (!response.ok) {
           const data = await response.json();
+          if (retries < MAX_RETRIES && response.status >= 500) {
+            await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
+            return fetchWithRetry(retries + 1);
+          }
           setMessages(prev => [...prev, { role: 'assistant', content: "Error: " + data.error }]);
           return;
         }
+        return response;
+      } catch (error) {
+        if (retries < MAX_RETRIES) {
+          await new Promise(resolve => setTimeout(resolve, RETRY_DELAY));
+          return fetchWithRetry(retries + 1);
+        }
+        throw error;
+      }
+    };

194-206: Consider enhancing markdown support

The current implementation only handles bold text. Consider supporting more markdown features for better content formatting.

 const parseContent = (content: string) => {
-    const formattedContent = content.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
+    const formattedContent = content
+      .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>')
+      .replace(/\*(.*?)\*/g, '<i>$1</i>')
+      .replace(/`(.*?)`/g, '<code>$1</code>')
+      .replace(/^- (.*?)$/gm, '<li>$1</li>')
+      .replace(/((?:^- .*?\n)+)/gm, '<ul>$1</ul>');

     const paragraphWrapped = formattedContent
         .split('\n\n')
         .map((line) => `<p>${line.trim()}</p>`)
         .join('');

     return DOMPurify.sanitize(paragraphWrapped);
 };

144-154: Consider enhancing loading state feedback

The current loading state could provide more detailed feedback about the request progress.

-                <Loader2 className="animate-spin " size={18} />
-                <span>AI is thinking...</span>
+                <Loader2 className="animate-spin" size={18} />
+                <span>
+                    {isLoading ? (
+                        <span className="inline-flex items-center">
+                            <span className="loading-dot">.</span>
+                            <span className="loading-dot">.</span>
+                            <span className="loading-dot">.</span>
+                        </span>
+                    ) : null}
+                </span>
+                <span>Processing your request</span>

Add this CSS for the loading animation:

.loading-dot {
    animation: loadingDots 1.4s infinite;
    animation-fill-mode: both;
}
.loading-dot:nth-child(2) { animation-delay: .2s; }
.loading-dot:nth-child(3) { animation-delay: .4s; }

@keyframes loadingDots {
    0% { opacity: 0.2; }
    20% { opacity: 1; }
    100% { opacity: 0.2; }
}
src/hooks/use-toast.ts (3)

11-11: Consider allowing multiple toasts by increasing TOAST_LIMIT

The TOAST_LIMIT is currently set to 1, which restricts the display to a single toast notification at a time. If the application's design permits, consider increasing this limit to allow multiple toasts to be displayed simultaneously, enhancing user feedback.


12-12: Review the TOAST_REMOVE_DELAY duration

The TOAST_REMOVE_DELAY is set to 1000000 milliseconds (approximately 16 minutes and 40 seconds). This duration is unusually long for toast notifications and may result in toasts persisting longer than intended. Consider reducing this delay to a more standard duration (e.g., 3000 milliseconds) to improve user experience.


30-33: Consider using UUIDs for unique toast IDs

The genId() function generates IDs using a simple incrementing counter. While effective in single-threaded environments, there is a potential risk of ID collisions in asynchronous contexts. Consider using UUIDs or a more robust ID generation method to ensure uniqueness across all instances.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4b2d930 and 6076781.

📒 Files selected for processing (5)
  • src/app/api/summarize/route.ts (1 hunks)
  • src/app/blog/[slug]/page.tsx (1 hunks)
  • src/components/BlogChatInterface.tsx (3 hunks)
  • src/components/ui/toast.tsx (1 hunks)
  • src/hooks/use-toast.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/blog/[slug]/page.tsx
🧰 Additional context used
📓 Learnings (1)
src/components/BlogChatInterface.tsx (1)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/BlogChatInterface.tsx:37-42
Timestamp: 2024-12-06T20:38:27.090Z
Learning: In the `src/components/BlogChatInterface.tsx` file of our React application, the `toast` function supports the properties `color`, `duration`, and `style`, allowing customization of toast notifications.
🔇 Additional comments (2)
src/components/BlogChatInterface.tsx (1)

37-43: LGTM! Toast implementation is correct

The toast implementation with custom styling properties is confirmed to work as expected based on previous learnings.

src/components/ui/toast.tsx (1)

1-129: Well-structured implementation of toast UI components

The toast components are effectively implemented using Radix UI primitives and adhere to best practices. The code is clean, well-organized, and promotes readability and maintainability.

@Rahulsoni9321
Copy link
Author

Hey @tyaga001 reviewed all the suggestions made by @coderabbitai and implented it along with all the updates.
have a look and let me know if any changes is needed.

Copy link
Contributor

coderabbitai bot commented Dec 6, 2024

@Rahulsoni9321: Great to hear that you've implemented the suggestions! I'll review the changes and let you know if any further updates are needed.


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nirnejak
Copy link
Collaborator

nirnejak commented Dec 28, 2024

Hey @Rahulsoni9321, can you please pull from main and resolve conflicts

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: 3

🧹 Nitpick comments (7)
src/app/layout.tsx (2)

12-12: Fix quote style for consistency

The import statement uses single quotes while the codebase convention appears to use double quotes.

-import { Toaster } from '@/components/ui/toaster';
+import { Toaster } from "@/components/ui/toaster"
🧰 Tools
🪛 eslint

[error] 12-13: Replace '@/components/ui/toaster';⏎ with "@/components/ui/toaster"

(prettier/prettier)


61-91: Run Prettier to fix formatting

There are multiple formatting inconsistencies in the indentation. Consider running Prettier to automatically fix these issues.

You can run:

npx prettier --write src/app/layout.tsx
🧰 Tools
🪛 eslint

[error] 61-61: Delete ··

(prettier/prettier)


[error] 62-62: Delete ····

(prettier/prettier)


[error] 63-63: Delete ······

(prettier/prettier)


[error] 64-64: Replace ················ with ········

(prettier/prettier)


[error] 65-65: Replace ···················· with ··········

(prettier/prettier)


[error] 66-66: Replace ····················description={metadata.description·??·'Learn·about·awesome·developer·tools'} with ··········description={⏎············metadata.description·??·"Learn·about·awesome·developer·tools"

(prettier/prettier)


[error] 67-67: Replace ··········url={metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'} with }⏎··········url={⏎············metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"

(prettier/prettier)


[error] 68-68: Replace ··········image={${metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'` with }⏎··········image={${metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"`

(prettier/prettier)


[error] 69-69: Replace ···················· with ··········

(prettier/prettier)


[error] 70-70: Delete ········

(prettier/prettier)


[error] 72-72: Replace ················ with ········

(prettier/prettier)


[error] 73-73: Delete ··········

(prettier/prettier)


[error] 74-74: Delete ··········

(prettier/prettier)


[error] 75-75: Replace ···················· with ··········

(prettier/prettier)


[error] 76-76: Replace ················ with ········

(prettier/prettier)


[error] 77-77: Delete ······

(prettier/prettier)


[error] 78-78: Replace ······<body·className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}> with <body⏎········className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}

(prettier/prettier)


[error] 79-79: Replace ·· with >⏎

(prettier/prettier)


[error] 80-80: Replace ···················· with ··········

(prettier/prettier)


[error] 81-81: Replace ························ with ············

(prettier/prettier)


[error] 82-82: Delete ············

(prettier/prettier)


[error] 83-83: Replace ···························· with ··············

(prettier/prettier)


[error] 84-84: Replace ························ with ············

(prettier/prettier)


[error] 85-85: Replace ···················· with ··········

(prettier/prettier)


[error] 86-86: Replace ···················· with ··········

(prettier/prettier)


[error] 87-87: Replace ················ with ········

(prettier/prettier)


[error] 88-88: Replace ················ with ········

(prettier/prettier)


[error] 89-89: Replace ············ with ······

(prettier/prettier)


[error] 90-90: Replace ········ with ····

(prettier/prettier)


[error] 91-91: Replace ··); with )

(prettier/prettier)

src/app/api/summarize/route.ts (1)

9-11: Consider enhancing error message for API key validation

The current error message is generic. Consider providing more specific guidance.

 if (!GEMINI_API_KEY) {
-  throw new Error("Something went wrong while fetching API Keys.")
+  throw new Error("GEMINI_API_KEY environment variable is not configured. Please check your environment setup.")
 }
src/components/BlogPostClient.tsx (1)

35-38: Consider memoizing the formatDate function

Since the date formatting operation is potentially expensive and the input-output mapping is deterministic, consider memoizing this function to improve performance.

+const memoizedFormatDate = React.useMemo(() => {
     const formatDate = (dateString: string) => {
         const date = new Date(dateString);
         return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
     };
+    return formatDate;
+}, []);
🧰 Tools
🪛 eslint

[error] 36-36: Replace ····const·date·=·new·Date(dateString); with const·date·=·new·Date(dateString)

(prettier/prettier)


[error] 37-37: Replace ····return·date.toLocaleDateString('en-US',·{·year:·'numeric',·month:·'long',·day:·'numeric'·}); with return·date.toLocaleDateString("en-US",·{⏎······year:·"numeric",⏎······month:·"long",⏎······day:·"numeric",⏎····})

(prettier/prettier)


[error] 38-38: Replace ····}; with ··}

(prettier/prettier)

tailwind.config.ts (1)

58-65: Consider adding will-change hint for performance

The shimmer animation involves background-position changes. Adding a will-change hint could improve performance.

 "shimmer": {
   from: {
-    "backgroundPosition": "0 0"
+    "backgroundPosition": "0 0",
+    "willChange": "background-position"
   },
   to: {
-    "backgroundPosition": "-200% 0"
+    "backgroundPosition": "-200% 0",
+    "willChange": "background-position"
   }
 },
🧰 Tools
🪛 eslint

[error] 58-58: Replace "shimmer" with shimmer

(prettier/prettier)


[error] 60-60: Replace "backgroundPosition":·"0·0" with backgroundPosition:·"0·0",

(prettier/prettier)


[error] 63-63: Replace "backgroundPosition":·"-200%·0" with backgroundPosition:·"-200%·0",

(prettier/prettier)


[error] 64-64: Insert ,

(prettier/prettier)

src/components/BlogChatInterface.tsx (2)

194-206: Optimize content parsing performance

The content parsing function could be more efficient by caching the regular expression and reducing string operations.

Apply this diff to improve performance:

+ const BOLD_PATTERN = /\*\*(.*?)\*\*/g;
+
 const parseContent = (content: string) => {
-    const formattedContent = content.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
+    const formattedContent = content.replace(BOLD_PATTERN, '<b>$1</b>');
 
     const paragraphWrapped = formattedContent
         .split('\n\n')
-        .map((line) => `<p>${line.trim()}</p>`)
-        .join('');
+        .map(line => line.trim())
+        .filter(Boolean)
+        .map(line => `<p>${line}</p>`)
+        .join('');
 
     return DOMPurify.sanitize(paragraphWrapped);
 };
🧰 Tools
🪛 eslint

[error] 195-195: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>'); with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 197-198: Delete ⏎··

(prettier/prettier)


[error] 199-199: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 200-200: Delete ····

(prettier/prettier)


[error] 201-201: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 203-205: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)


22-30: Add cleanup for scroll effect

The scroll effect should be cleaned up when the component unmounts to prevent potential memory leaks.

Apply this diff to add proper cleanup:

- useEffect(scrollToBottom, [messages]);
+ useEffect(() => {
+    let mounted = true;
+    if (mounted) {
+        scrollToBottom();
+    }
+    return () => {
+        mounted = false;
+    };
+ }, [messages]);
🧰 Tools
🪛 eslint

[error] 22-22: Replace ·blogContent,·blogTitle,·blogDescription,·onClose· with ⏎··blogContent,⏎··blogTitle,⏎··blogDescription,⏎··onClose,⏎

(prettier/prettier)


[error] 23-23: Replace ····const·[messages,·setMessages]·=·useState<ChatMessage[]>([]); with ··const·[messages,·setMessages]·=·useState<ChatMessage[]>([])

(prettier/prettier)


[error] 24-24: Replace ····const·[input,·setInput]·=·useState(''); with ··const·[input,·setInput]·=·useState("")

(prettier/prettier)


[error] 25-25: Replace ····const·[isLoading,·setIsLoading]·=·useState(false); with ··const·[isLoading,·setIsLoading]·=·useState(false)

(prettier/prettier)


[error] 26-26: Replace ··const·messagesEndRef·=·useRef<HTMLDivElement>(null); with const·messagesEndRef·=·useRef<HTMLDivElement>(null)

(prettier/prettier)


[error] 27-27: Replace ····const·{·toast·}·=·useToast(); with ··const·{·toast·}·=·useToast()

(prettier/prettier)


[error] 28-28: Replace ···· with ··

(prettier/prettier)


[error] 29-29: Replace ········messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·}); with ····messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·})

(prettier/prettier)


[error] 30-30: Replace ····}; with ··}

(prettier/prettier)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6076781 and 421f2ac.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (8)
  • package.json (3 hunks)
  • src/app/api/summarize/route.ts (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/components/BlogChatInterface.tsx (1 hunks)
  • src/components/BlogPostClient.tsx (2 hunks)
  • src/components/Navbar.tsx (1 hunks)
  • tailwind.config.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/globals.css
  • src/components/Navbar.tsx
  • package.json
🧰 Additional context used
📓 Learnings (1)
src/components/BlogChatInterface.tsx (1)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/BlogChatInterface.tsx:37-42
Timestamp: 2024-12-06T20:38:27.090Z
Learning: In the `src/components/BlogChatInterface.tsx` file of our React application, the `toast` function supports the properties `color`, `duration`, and `style`, allowing customization of toast notifications.
🪛 eslint
src/components/BlogPostClient.tsx

[error] 3-3: Replace 'react'; with "react"

(prettier/prettier)


[error] 4-4: Replace '@/components/BlogChatInterface'; with "@/components/BlogChatInterface"

(prettier/prettier)


[error] 5-5: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 6-6: Replace '@/components/ViewCounter'; with "@/components/ViewCounter"

(prettier/prettier)


[error] 7-7: Replace '@/components/SocialMetadata'; with "@/components/SocialMetadata"

(prettier/prettier)


[error] 8-8: Replace '@/components/SocialShare'; with "@/components/SocialShare"

(prettier/prettier)


[error] 30-31: Delete ···⏎

(prettier/prettier)


[error] 32-35: Delete ⏎⏎··⏎····

(prettier/prettier)


[error] 36-36: Replace ····const·date·=·new·Date(dateString); with const·date·=·new·Date(dateString)

(prettier/prettier)


[error] 37-37: Replace ····return·date.toLocaleDateString('en-US',·{·year:·'numeric',·month:·'long',·day:·'numeric'·}); with return·date.toLocaleDateString("en-US",·{⏎······year:·"numeric",⏎······month:·"long",⏎······day:·"numeric",⏎····})

(prettier/prettier)


[error] 38-38: Replace ····}; with ··}

(prettier/prettier)


[error] 40-40: Replace ··const·baseUrl·=·'https://devtoolsacademy.com'; with const·baseUrl·=·"https://devtoolsacademy.com"

(prettier/prettier)


[error] 41-41: Replace ····const·postUrl·=·${baseUrl}/blog/${slug}; with ··const·postUrl·=·${baseUrl}/blog/${slug}``

(prettier/prettier)


[error] 43-43: Delete ··

(prettier/prettier)


[error] 44-44: Delete ····

(prettier/prettier)


[error] 45-45: Delete ······

(prettier/prettier)


[error] 46-46: Replace ················ with ········

(prettier/prettier)


[error] 47-47: Replace ················ with ········

(prettier/prettier)


[error] 48-48: Delete ········

(prettier/prettier)


[error] 49-49: Replace ················ with ········

(prettier/prettier)


[error] 50-50: Replace ················ with ········

(prettier/prettier)


[error] 51-51: Delete ······

(prettier/prettier)


[error] 52-52: Replace ············ with ······

(prettier/prettier)


[error] 53-53: Delete ······

(prettier/prettier)


[error] 54-54: Replace ················ with ········

(prettier/prettier)


[error] 55-55: Replace ····················<span·className="text-gray-400">{formatDate(publishedAt)}</span> with ··········<span·className="text-gray-400">{formatDate(publishedAt)}</span>{"·"}⏎·········

(prettier/prettier)


[error] 56-56: Replace ··········<button·onClick={()=>setShowChat(true)}·className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"> with <button⏎············onClick={()·=>·setShowChat(true)}⏎············className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"

(prettier/prettier)


[error] 57-57: Insert ··>⏎

(prettier/prettier)


[error] 58-58: Insert ··

(prettier/prettier)


[error] 59-59: Replace ················ with ········

(prettier/prettier)


[error] 60-60: Delete ········

(prettier/prettier)


[error] 61-61: Delete ··········

(prettier/prettier)


[error] 62-62: Replace ···················· with ··········

(prettier/prettier)


[error] 63-63: Replace ················ with ········

(prettier/prettier)


[error] 64-64: Replace ············ with ······

(prettier/prettier)


[error] 65-65: Delete ······

(prettier/prettier)


[error] 66-66: Delete ······

(prettier/prettier)


[error] 67-67: Delete ········

(prettier/prettier)


[error] 68-68: Replace ···················· with ··········

(prettier/prettier)


[error] 69-69: Replace ···················· with ··········

(prettier/prettier)


[error] 70-70: Replace ···················· with ··········

(prettier/prettier)


[error] 71-71: Replace ···················· with ··········

(prettier/prettier)


[error] 72-72: Delete ········

(prettier/prettier)


[error] 73-73: Replace ············ with ······

(prettier/prettier)


[error] 74-74: Replace ········ with ····

(prettier/prettier)


[error] 75-75: Replace ····); with ··)

(prettier/prettier)


[error] 76-76: Delete ;

(prettier/prettier)

src/app/api/summarize/route.ts

[error] 4-5: Delete ;⏎

(prettier/prettier)


[error] 7-7: Delete ;

(prettier/prettier)


[error] 12-12: Delete ;

(prettier/prettier)


[error] 15-15: Replace ·"You're·an·AI·assistant,·Who·only·answers·questions·related·to·the·blog·post·that·have·provided·to·you·in·the·prompt.\n\nTry·to·Answer·using·numeric·points·for·readability·and·Bold·the·title·of·the·point.·\n\n·If·the·user·wants·help·related·to·the·topic·mentioned·in·the·blog·you·can·answer·that.\n\n·If·the·question·is·not·related·to·the·blog·post·or·the·topic·that·is·mentioned·on·the·blog,·Throw·error·response." with ⏎····"You're·an·AI·assistant,·Who·only·answers·questions·related·to·the·blog·post·that·have·provided·to·you·in·the·prompt.\n\nTry·to·Answer·using·numeric·points·for·readability·and·Bold·the·title·of·the·point.·\n\n·If·the·user·wants·help·related·to·the·topic·mentioned·in·the·blog·you·can·answer·that.\n\n·If·the·question·is·not·related·to·the·blog·post·or·the·topic·that·is·mentioned·on·the·blog,·Throw·error·response.",

(prettier/prettier)


[error] 16-18: Replace ⏎⏎); with )

(prettier/prettier)


[error] 21-21: Delete ;

(prettier/prettier)


[error] 23-23: Replace !body.content·||·typeof·body.content·!==·'string'·||·!body.query·||·typeof·body.query·!==·'string' with ⏎····!body.content·||⏎····typeof·body.content·!==·"string"·||⏎····!body.query·||⏎····typeof·body.query·!==·"string"⏎··

(prettier/prettier)


[error] 25-25: Replace ·error:·'Invalid·request:·content·and·query·are·required·and·must·be·strings',·success:·false with ⏎········error:⏎··········"Invalid·request:·content·and·query·are·required·and·must·be·strings",⏎········success:·false,⏎·····

(prettier/prettier)


[error] 27-27: Delete ;

(prettier/prettier)


[error] 30-30: Delete ;

(prettier/prettier)


[error] 33-33: Delete ;

(prettier/prettier)


[error] 35-35: Delete ;

(prettier/prettier)


[error] 39-40: Delete ⏎·

(prettier/prettier)


[error] 41-41: Delete ;

(prettier/prettier)


[error] 43-43: Replace 'API·Error:' with "API·Error:"

(prettier/prettier)


[error] 44-44: Replace 'GoogleGenerativeAI'·:·'Unknown' with "GoogleGenerativeAI"·:·"Unknown"

(prettier/prettier)


[error] 46-46: Insert ,

(prettier/prettier)


[error] 47-47: Delete ;

(prettier/prettier)


[error] 50-50: Replace 'The·AI·service·is·temporarily·unavailable.·Please·try·again·later.' with "The·AI·service·is·temporarily·unavailable.·Please·try·again·later."

(prettier/prettier)


[error] 51-51: Replace 'Internal·Server·Error.·Please·try·again·after·some·time.'; with "Internal·Server·Error.·Please·try·again·after·some·time."

(prettier/prettier)


[error] 55-55: Insert ,

(prettier/prettier)


[error] 58-58: Delete ;

(prettier/prettier)

src/components/BlogChatInterface.tsx

[error] 1-1: Replace 'react'; with "react"

(prettier/prettier)


[error] 2-2: Replace 'framer-motion'; with "framer-motion"

(prettier/prettier)


[error] 3-3: Replace 'lucide-react'; with "lucide-react"

(prettier/prettier)


[error] 4-4: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 5-5: Replace './ChatSuggestions'; with "./ChatSuggestions"

(prettier/prettier)


[error] 6-6: Replace 'dompurify'; with "dompurify"

(prettier/prettier)


[error] 7-7: Delete ;

(prettier/prettier)


[error] 8-8: Replace '@/hooks/use-toast'; with "@/hooks/use-toast"

(prettier/prettier)


[error] 16-16: Replace ····blogContent:·string; with ··blogContent:·string

(prettier/prettier)


[error] 17-17: Replace ····blogTitle:·string; with ··blogTitle:·string

(prettier/prettier)


[error] 18-18: Replace ····blogDescription:·string; with ··blogDescription:·string

(prettier/prettier)


[error] 19-19: Replace ····onClose:·()·=>·void; with ··onClose:·()·=>·void

(prettier/prettier)


[error] 22-22: Replace ·blogContent,·blogTitle,·blogDescription,·onClose· with ⏎··blogContent,⏎··blogTitle,⏎··blogDescription,⏎··onClose,⏎

(prettier/prettier)


[error] 23-23: Replace ····const·[messages,·setMessages]·=·useState<ChatMessage[]>([]); with ··const·[messages,·setMessages]·=·useState<ChatMessage[]>([])

(prettier/prettier)


[error] 24-24: Replace ····const·[input,·setInput]·=·useState(''); with ··const·[input,·setInput]·=·useState("")

(prettier/prettier)


[error] 25-25: Replace ····const·[isLoading,·setIsLoading]·=·useState(false); with ··const·[isLoading,·setIsLoading]·=·useState(false)

(prettier/prettier)


[error] 26-26: Replace ··const·messagesEndRef·=·useRef<HTMLDivElement>(null); with const·messagesEndRef·=·useRef<HTMLDivElement>(null)

(prettier/prettier)


[error] 27-27: Replace ····const·{·toast·}·=·useToast(); with ··const·{·toast·}·=·useToast()

(prettier/prettier)


[error] 28-28: Replace ···· with ··

(prettier/prettier)


[error] 29-29: Replace ········messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·}); with ····messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·})

(prettier/prettier)


[error] 30-30: Replace ····}; with ··}

(prettier/prettier)


[error] 34-34: Delete ··

(prettier/prettier)


[error] 35-35: Replace ········const·promptToSend·=·customPrompt·||·input; with ····const·promptToSend·=·customPrompt·||·input

(prettier/prettier)


[error] 36-36: Delete ····

(prettier/prettier)


[error] 37-37: Delete ······

(prettier/prettier)


[error] 38-38: Replace ········variant:·'destructive' with variant:·"destructive"

(prettier/prettier)


[error] 39-39: Replace ················color:·'gray' with ········color:·"gray"

(prettier/prettier)


[error] 40-40: Replace ················ with ········

(prettier/prettier)


[error] 41-41: Replace ········style:·{·background:·'#7f1d1d',·color:·'white',·border:·'gray',·borderRadius:·'5px'·} with style:·{⏎··········background:·"#7f1d1d",⏎··········color:·"white",⏎··········border:·"gray",⏎··········borderRadius:·"5px"

(prettier/prettier)


[error] 42-42: Replace ········description:·"Please·enter·valid·input." with },⏎········description:·"Please·enter·valid·input.",

(prettier/prettier)


[error] 43-43: Replace ············ with ······

(prettier/prettier)


[error] 45-45: Replace ············return; with ······return

(prettier/prettier)


[error] 46-46: Replace ····}; with }

(prettier/prettier)


[error] 48-48: Replace ········const·userMessage:·ChatMessage·=·{·role:·'human',·content:·promptToSend·}; with ····const·userMessage:·ChatMessage·=·{·role:·"human",·content:·promptToSend·}

(prettier/prettier)


[error] 49-49: Replace ········setMessages(prev·=>·[...prev,·userMessage]); with ····setMessages((prev)·=>·[...prev,·userMessage])

(prettier/prettier)


[error] 50-50: Replace ········setInput(''); with ····setInput("")

(prettier/prettier)


[error] 51-51: Replace ········setIsLoading(true); with ····setIsLoading(true)

(prettier/prettier)


[error] 53-53: Delete ····

(prettier/prettier)


[error] 54-54: Replace ······const·response·=·await·fetch('/api/summarize' with const·response·=·await·fetch("/api/summarize"

(prettier/prettier)


[error] 55-55: Replace ················method:·'POST' with ········method:·"POST"

(prettier/prettier)


[error] 56-56: Replace ················headers:·{·'Content-Type':·'application/json' with ········headers:·{·"Content-Type":·"application/json"

(prettier/prettier)


[error] 57-57: Delete ········

(prettier/prettier)


[error] 58-59: Replace ······});⏎ with })

(prettier/prettier)


[error] 61-62: Delete ⏎······

(prettier/prettier)


[error] 63-63: Replace ················const·data·=·await·response.json(); with ········const·data·=·await·response.json()

(prettier/prettier)


[error] 64-64: Replace ················setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·"Error:·"·+·data.error·}]); with ········setMessages((prev)·=>·[⏎··········...prev,⏎··········{·role:·"assistant",·content:·"Error:·"·+·data.error·},

(prettier/prettier)


[error] 65-65: Replace ········return; with ])⏎········return

(prettier/prettier)


[error] 66-67: Delete ⏎······

(prettier/prettier)


[error] 68-68: Replace ············const·data·=·await·response.json(); with ······const·data·=·await·response.json()

(prettier/prettier)


[error] 70-70: Replace ············const·responseAnswer·=·parseContent(data.answer); with ······const·responseAnswer·=·parseContent(data.answer)

(prettier/prettier)


[error] 72-72: Replace ············setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·responseAnswer·}]); with ······setMessages((prev)·=>·[⏎········...prev,⏎········{·role:·"assistant",·content:·responseAnswer·},

(prettier/prettier)


[error] 73-73: Insert ])⏎··

(prettier/prettier)


[error] 74-74: Replace ······console.error('Error·in·chat:',·error); with console.error("Error·in·chat:",·error)

(prettier/prettier)


[error] 75-75: Replace ············setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·"I·have·encountered·some·error.·Please·try·again."·}]); with ······setMessages((prev)·=>·[⏎········...prev,⏎········{⏎··········role:·"assistant",⏎··········content:·"I·have·encountered·some·error.·Please·try·again.",⏎········},

(prettier/prettier)


[error] 76-76: Insert ])⏎··

(prettier/prettier)


[error] 77-77: Replace ············setIsLoading(false); with ······setIsLoading(false)

(prettier/prettier)


[error] 78-78: Replace ········ with ····

(prettier/prettier)


[error] 79-79: Replace ····}; with ··}

(prettier/prettier)


[error] 80-80: Delete ··

(prettier/prettier)


[error] 81-84: Replace ⏎⏎········handleSend("Please·Summarize·this·blog·for·me.")⏎ with ····handleSend("Please·Summarize·this·blog·for·me.")

(prettier/prettier)


[error] 85-85: Delete ··

(prettier/prettier)


[error] 86-86: Delete ··

(prettier/prettier)


[error] 87-87: Replace ········ with ····

(prettier/prettier)


[error] 88-88: Replace ············ with ······

(prettier/prettier)


[error] 89-89: Replace ············ with ······

(prettier/prettier)


[error] 90-90: Delete ······

(prettier/prettier)


[error] 91-91: Delete ······

(prettier/prettier)


[error] 92-92: Delete ····

(prettier/prettier)


[error] 93-93: Replace ············ with ······

(prettier/prettier)


[error] 94-94: Delete ········

(prettier/prettier)


[error] 95-95: Delete ········

(prettier/prettier)


[error] 96-96: Replace ················ with ········

(prettier/prettier)


[error] 97-97: Delete ······

(prettier/prettier)


[error] 98-98: Replace ················ with ········

(prettier/prettier)


[error] 99-99: Replace ····················<h2·className="text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">Chat·about·the·Blog·<ChatBubbleLeftIcon·className='w-7·h-7'></ChatBubbleLeftIcon></h2> with ··········<h2·className="text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">⏎············Chat·about·the·Blog{"·"}⏎············<ChatBubbleLeftIcon·className="w-7·h-7"></ChatBubbleLeftIcon>

(prettier/prettier)


[error] 100-100: Insert </h2>⏎

(prettier/prettier)


[error] 101-101: Delete ············

(prettier/prettier)


[error] 102-102: Replace ···················· with ··········

(prettier/prettier)


[error] 103-103: Replace ················ with ········

(prettier/prettier)


[error] 104-104: Delete ········

(prettier/prettier)


[error] 105-105: Delete ··········

(prettier/prettier)


[error] 106-106: Replace ························{messages.length·==·0·?·<ChatSuggestions·title={blogTitle}·description={blogDescription}·handleSend={handleSend}></ChatSuggestions·>·:· with ············{messages.length·==·0·?·(⏎··············<ChatSuggestions⏎················title={blogTitle}⏎················description={blogDescription}⏎················handleSend={handleSend}⏎··············></ChatSuggestions>⏎············)·:·(⏎··············

(prettier/prettier)


[error] 107-107: Replace ············<motion.div·key={index} with <motion.div

(prettier/prettier)


[error] 108-108: Insert key={index}⏎····

(prettier/prettier)


[error] 109-109: Delete ··············

(prettier/prettier)


[error] 110-110: Replace ··············exit={{·opacity:·0,·y:·-20·}}·className='flex·flex-col··gap-4·'> with exit={{·opacity:·0,·y:·-20·}}⏎··················className="flex·flex-col··gap-4·"

(prettier/prettier)


[error] 111-111: Replace ················<div·className='flex·gap-4·items-start' with >⏎··················<div·className="flex·gap-4·items-start"

(prettier/prettier)


[error] 112-112: Replace ····································<div·className='min-w-12·min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·rounded-full·bg-black' with ····················<div·className="min-w-12·min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·rounded-full·bg-black"

(prettier/prettier)


[error] 113-114: Replace ········································{msg.role·===·"human"·?·<User></User>·:·<BotIcon></BotIcon>}⏎····································</div with ······················{msg.role·===·"human"·?·(⏎························<User></User>⏎······················)·:·(⏎························<BotIcon></BotIcon

(prettier/prettier)


[error] 115-115: Replace ···············<motion.div with ·)}⏎····················</div>

(prettier/prettier)


[error] 116-116: Insert ··<motion.div⏎

(prettier/prettier)


[error] 117-117: Replace ········································ with ······················

(prettier/prettier)


[error] 118-118: Replace ········································ with ······················

(prettier/prettier)


[error] 119-119: Replace ········································className={flex·${msg.role·===·'human'·?·'justify-end'·:·'justify-start'with······················className={flex·${msg.role·===·"human"·?·"justify-end"·:·"justify-start"

(prettier/prettier)


[error] 120-120: Replace ····································> with ····················>⏎······················<div

(prettier/prettier)


[error] 121-121: Replace ········································<div·className={max-w-full·text-[16px]·leading-relaxed·tracking-wide·shadow-lg·shadow-black·px-5·py-4·rounded-xl·${msg.role·===·'human'·?·'bg-white/80·text-black/80'·:·'bg-[#09090b]·text-gray-200'with························className={max-w-full·text-[16px]·leading-relaxed·tracking-wide·shadow-lg·shadow-black·px-5·py-4·rounded-xl·${⏎··························msg.role·===·"human"⏎····························?·"bg-white/80·text-black/80"⏎····························:·"bg-[#09090b]·text-gray-200"

(prettier/prettier)


[error] 122-122: Replace ············································ with ························

(prettier/prettier)


[error] 123-123: Replace ········································ with ······················

(prettier/prettier)


[error] 124-124: Replace ············································{msg.role·===·'human' with ························{msg.role·===·"human"

(prettier/prettier)


[error] 125-127: Replace ················································<>⏎····················································{msg.content}⏎················································ with ··························<>{msg.content}

(prettier/prettier)


[error] 128-128: Replace ············································ with ························

(prettier/prettier)


[error] 129-131: Replace ················································<>⏎····················································{parse(msg.content)}⏎················································ with ··························<>{parse(msg.content)}

(prettier/prettier)


[error] 132-132: Replace ············································ with ························

(prettier/prettier)


[error] 133-133: Replace ········································ with ······················

(prettier/prettier)


[error] 134-134: Replace ···································· with ····················

(prettier/prettier)


[error] 135-135: Replace ································ with ··················

(prettier/prettier)


[error] 136-136: Replace ··············<div·className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}></div> with <div⏎····················className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}

(prettier/prettier)


[error] 137-137: Replace ············ with ··></div>⏎················

(prettier/prettier)


[error] 138-138: Replace ··········))} with ))

(prettier/prettier)


[error] 139-139: Replace ········· with ·)}⏎··········

(prettier/prettier)


[error] 140-140: Replace ···················· with ··········

(prettier/prettier)


[error] 141-141: Replace ························ with ············

(prettier/prettier)


[error] 142-142: Delete ··············

(prettier/prettier)


[error] 143-143: Replace ···························· with ··············

(prettier/prettier)


[error] 144-144: Replace ···························· with ··············

(prettier/prettier)


[error] 145-145: Replace ························ with ············

(prettier/prettier)


[error] 146-146: Replace ····························<div·className='min-w-12·min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·bg-black' with ··············<div·className="min-w-12·min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·bg-black"

(prettier/prettier)


[error] 147-147: Delete ················

(prettier/prettier)


[error] 148-148: Delete ··············

(prettier/prettier)


[error] 149-149: Replace ···························· with ··············

(prettier/prettier)


[error] 150-151: Delete ⏎················

(prettier/prettier)


[error] 152-152: Replace ································ with ················

(prettier/prettier)


[error] 153-153: Delete ··············

(prettier/prettier)


[error] 154-154: Replace ························ with ············

(prettier/prettier)


[error] 155-155: Delete ··········

(prettier/prettier)


[error] 156-156: Delete ··········

(prettier/prettier)


[error] 157-157: Delete ········

(prettier/prettier)


[error] 158-158: Replace ················ with ········

(prettier/prettier)


[error] 159-159: Delete ··········

(prettier/prettier)


[error] 160-160: Replace ························ with ············

(prettier/prettier)


[error] 161-161: Replace ···························· with ··············

(prettier/prettier)


[error] 162-162: Replace ···························· with ··············

(prettier/prettier)


[error] 163-163: Delete ··············

(prettier/prettier)


[error] 164-164: Replace ····························onKeyPress={(e)·=>·e.key·===·'Enter' with ··············onKeyPress={(e)·=>·e.key·===·"Enter"

(prettier/prettier)


[error] 165-165: Replace ···························· with ··············

(prettier/prettier)


[error] 166-166: Delete ··············

(prettier/prettier)


[error] 167-167: Delete ············

(prettier/prettier)


[error] 168-168: Replace ············<button·onClick={handleSummary}·className='bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black'>Summarize with <button⏎··············onClick={handleSummary}⏎··············className="bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black"⏎············>⏎··············Summarize{"·"}⏎···········

(prettier/prettier)


[error] 170-170: Replace ·············<button with ·<button⏎·············

(prettier/prettier)


[error] 171-171: Replace ····························disabled={isLoading}·className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"> with ··············disabled={isLoading}⏎··············className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"

(prettier/prettier)


[error] 172-172: Replace ·· with >⏎

(prettier/prettier)


[error] 173-173: Replace ································ with ················

(prettier/prettier)


[error] 174-174: Replace ···························· with ··············

(prettier/prettier)


[error] 175-175: Delete ··············

(prettier/prettier)


[error] 176-176: Delete ················

(prettier/prettier)


[error] 177-177: Replace ···································· with ··················

(prettier/prettier)


[error] 178-179: Replace ⏎································ with ················

(prettier/prettier)


[error] 180-181: Replace ⏎···························· with ··············

(prettier/prettier)


[error] 182-182: Delete ··············

(prettier/prettier)


[error] 183-183: Delete ············

(prettier/prettier)


[error] 184-185: Replace ⏎···················· with ··········

(prettier/prettier)


[error] 186-186: Replace ················ with ········

(prettier/prettier)


[error] 187-187: Replace ············ with ······

(prettier/prettier)


[error] 188-188: Replace ········ with ····

(prettier/prettier)


[error] 189-189: Replace ··); with )

(prettier/prettier)


[error] 190-192: Delete ;⏎⏎

(prettier/prettier)


[error] 195-195: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>'); with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 197-198: Delete ⏎··

(prettier/prettier)


[error] 199-199: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 200-200: Delete ····

(prettier/prettier)


[error] 201-201: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 203-205: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)


[error] 206-210: Delete ;⏎⏎⏎⏎

(prettier/prettier)


[error] 212-212: Replace ; with

(prettier/prettier)

src/app/layout.tsx

[error] 12-13: Replace '@/components/ui/toaster';⏎ with "@/components/ui/toaster"

(prettier/prettier)


[error] 61-61: Delete ··

(prettier/prettier)


[error] 62-62: Delete ····

(prettier/prettier)


[error] 63-63: Delete ······

(prettier/prettier)


[error] 64-64: Replace ················ with ········

(prettier/prettier)


[error] 65-65: Replace ···················· with ··········

(prettier/prettier)


[error] 66-66: Replace ····················description={metadata.description·??·'Learn·about·awesome·developer·tools'} with ··········description={⏎············metadata.description·??·"Learn·about·awesome·developer·tools"

(prettier/prettier)


[error] 67-67: Replace ··········url={metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'} with }⏎··········url={⏎············metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"

(prettier/prettier)


[error] 68-68: Replace ··········image={${metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'` with }⏎··········image={${metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"`

(prettier/prettier)


[error] 69-69: Replace ···················· with ··········

(prettier/prettier)


[error] 70-70: Delete ········

(prettier/prettier)


[error] 72-72: Replace ················ with ········

(prettier/prettier)


[error] 73-73: Delete ··········

(prettier/prettier)


[error] 74-74: Delete ··········

(prettier/prettier)


[error] 75-75: Replace ···················· with ··········

(prettier/prettier)


[error] 76-76: Replace ················ with ········

(prettier/prettier)


[error] 77-77: Delete ······

(prettier/prettier)


[error] 78-78: Replace ······<body·className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}> with <body⏎········className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}

(prettier/prettier)


[error] 79-79: Replace ·· with >⏎

(prettier/prettier)


[error] 80-80: Replace ···················· with ··········

(prettier/prettier)


[error] 81-81: Replace ························ with ············

(prettier/prettier)


[error] 82-82: Delete ············

(prettier/prettier)


[error] 83-83: Replace ···························· with ··············

(prettier/prettier)


[error] 84-84: Replace ························ with ············

(prettier/prettier)


[error] 85-85: Replace ···················· with ··········

(prettier/prettier)


[error] 86-86: Replace ···················· with ··········

(prettier/prettier)


[error] 87-87: Replace ················ with ········

(prettier/prettier)


[error] 88-88: Replace ················ with ········

(prettier/prettier)


[error] 89-89: Replace ············ with ······

(prettier/prettier)


[error] 90-90: Replace ········ with ····

(prettier/prettier)


[error] 91-91: Replace ··); with )

(prettier/prettier)

tailwind.config.ts

[error] 58-58: Replace "shimmer" with shimmer

(prettier/prettier)


[error] 60-60: Replace "backgroundPosition":·"0·0" with backgroundPosition:·"0·0",

(prettier/prettier)


[error] 63-63: Replace "backgroundPosition":·"-200%·0" with backgroundPosition:·"-200%·0",

(prettier/prettier)


[error] 64-64: Insert ,

(prettier/prettier)


[error] 66-66: Replace 'fade-in-down' with "fade-in-down"

(prettier/prettier)


[error] 67-67: Replace '0%' with "0%"

(prettier/prettier)


[error] 68-68: Replace '0' with "0"

(prettier/prettier)


[error] 69-69: Replace 'translateY(-10px)' with "translateY(-10px)",

(prettier/prettier)


[error] 104-104: Replace 'fade-in-down':·'fade-in-down·0.5s·ease-out' with "fade-in-down":·"fade-in-down·0.5s·ease-out"

(prettier/prettier)


[error] 106-106: Replace 'fade-in-up':·'fade-in-up·0.5s·ease-out' with "fade-in-up":·"fade-in-up·0.5s·ease-out"

(prettier/prettier)


[error] 107-107: Replace 'fade-in':·'fade-in·0.5s·ease-out' with "fade-in":·"fade-in·0.5s·ease-out"

(prettier/prettier)


[error] 108-108: Replace 'pulse-shadow':·'pulse-shadow·2s·infinite' with "pulse-shadow":·"pulse-shadow·2s·infinite",

(prettier/prettier)

🔇 Additional comments (9)
src/app/layout.tsx (2)

Line range hint 1-91: Implementation looks good overall!

The addition of the Toaster component aligns well with the PR's objective of improving error handling. The component is correctly positioned within the layout hierarchy to ensure proper overlay of notifications.

🧰 Tools
🪛 eslint

[error] 61-61: Delete ··

(prettier/prettier)


[error] 62-62: Delete ····

(prettier/prettier)


[error] 63-63: Delete ······

(prettier/prettier)


[error] 64-64: Replace ················ with ········

(prettier/prettier)


[error] 65-65: Replace ···················· with ··········

(prettier/prettier)


[error] 66-66: Replace ····················description={metadata.description·??·'Learn·about·awesome·developer·tools'} with ··········description={⏎············metadata.description·??·"Learn·about·awesome·developer·tools"

(prettier/prettier)


[error] 67-67: Replace ··········url={metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'} with }⏎··········url={⏎············metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"

(prettier/prettier)


[error] 68-68: Replace ··········image={${metadata.metadataBase?.toString()·??·'https://devtoolsacademy.com'` with }⏎··········image={${metadata.metadataBase?.toString()·??·"https://devtoolsacademy.com"`

(prettier/prettier)


[error] 69-69: Replace ···················· with ··········

(prettier/prettier)


[error] 70-70: Delete ········

(prettier/prettier)


[error] 72-72: Replace ················ with ········

(prettier/prettier)


[error] 73-73: Delete ··········

(prettier/prettier)


[error] 74-74: Delete ··········

(prettier/prettier)


[error] 75-75: Replace ···················· with ··········

(prettier/prettier)


[error] 76-76: Replace ················ with ········

(prettier/prettier)


[error] 77-77: Delete ······

(prettier/prettier)


[error] 78-78: Replace ······<body·className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}> with <body⏎········className={${GeistSans.variable}·${GeistMono.variable}·font-sans·antialiased}

(prettier/prettier)


[error] 79-79: Replace ·· with >⏎

(prettier/prettier)


[error] 80-80: Replace ···················· with ··········

(prettier/prettier)


[error] 81-81: Replace ························ with ············

(prettier/prettier)


[error] 82-82: Delete ············

(prettier/prettier)


[error] 83-83: Replace ···························· with ··············

(prettier/prettier)


[error] 84-84: Replace ························ with ············

(prettier/prettier)


[error] 85-85: Replace ···················· with ··········

(prettier/prettier)


[error] 86-86: Replace ···················· with ··········

(prettier/prettier)


[error] 87-87: Replace ················ with ········

(prettier/prettier)


[error] 88-88: Replace ················ with ········

(prettier/prettier)


[error] 89-89: Replace ············ with ······

(prettier/prettier)


[error] 90-90: Replace ········ with ····

(prettier/prettier)


[error] 91-91: Replace ··); with )

(prettier/prettier)


86-86: Verify Toaster component implementation

The Toaster component is correctly positioned for overlay notifications. However, let's verify its implementation.

✅ Verification successful

Let me verify the toast hook implementation to ensure it's properly connected.


Toast implementation is properly configured and integrated

The Toaster component is correctly implemented with all necessary dependencies:

  • The toast hook (use-toast.ts) provides state management with proper lifecycle handling
  • Base Toast components are built on Radix UI primitives with proper styling and accessibility
  • The Toaster component in toaster.tsx correctly uses the hook and renders toast notifications
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Toaster component and its dependencies exist
echo "Checking Toaster component and dependencies..."
fd "toast" "src/components/ui" --type f
fd "use-toast.ts" "src/hooks" --type f

# Verify the component structure
echo -e "\nVerifying Toaster implementation..."
ast-grep --pattern 'export function Toaster() {
  $$$
}'

Length of output: 1758


Script:

#!/bin/bash
# Check the toast hook implementation
echo "Checking use-toast hook implementation..."
cat src/hooks/use-toast.ts

# Verify Toast component implementation
echo -e "\nChecking Toast component implementation..."
cat src/components/ui/toast.tsx

Length of output: 9005

🧰 Tools
🪛 eslint

[error] 86-86: Replace ···················· with ··········

(prettier/prettier)

src/app/api/summarize/route.ts (3)

23-28: LGTM! Good input validation.

The request validation is thorough and returns appropriate error responses.

🧰 Tools
🪛 eslint

[error] 23-23: Replace !body.content·||·typeof·body.content·!==·'string'·||·!body.query·||·typeof·body.query·!==·'string' with ⏎····!body.content·||⏎····typeof·body.content·!==·"string"·||⏎····!body.query·||⏎····typeof·body.query·!==·"string"⏎··

(prettier/prettier)


[error] 25-25: Replace ·error:·'Invalid·request:·content·and·query·are·required·and·must·be·strings',·success:·false with ⏎········error:⏎··········"Invalid·request:·content·and·query·are·required·and·must·be·strings",⏎········success:·false,⏎·····

(prettier/prettier)


[error] 27-27: Delete ;

(prettier/prettier)


40-58: LGTM! Comprehensive error handling.

The error handling differentiates between different types of errors, provides user-friendly messages, and includes detailed logging.

🧰 Tools
🪛 eslint

[error] 41-41: Delete ;

(prettier/prettier)


[error] 43-43: Replace 'API·Error:' with "API·Error:"

(prettier/prettier)


[error] 44-44: Replace 'GoogleGenerativeAI'·:·'Unknown' with "GoogleGenerativeAI"·:·"Unknown"

(prettier/prettier)


[error] 46-46: Insert ,

(prettier/prettier)


[error] 47-47: Delete ;

(prettier/prettier)


[error] 50-50: Replace 'The·AI·service·is·temporarily·unavailable.·Please·try·again·later.' with "The·AI·service·is·temporarily·unavailable.·Please·try·again·later."

(prettier/prettier)


[error] 51-51: Replace 'Internal·Server·Error.·Please·try·again·after·some·time.'; with "Internal·Server·Error.·Please·try·again·after·some·time."

(prettier/prettier)


[error] 55-55: Insert ,

(prettier/prettier)


[error] 58-58: Delete ;

(prettier/prettier)


33-38: ⚠️ Potential issue

Add input sanitization for security

Direct concatenation of user input into the prompt could lead to prompt injection attacks.

-const prompt = `Here's a blog post:\n\n${content}\n\nQuestion: ${query}`;
+const sanitizedContent = content.replace(/[<>]/g, '');
+const sanitizedQuery = query.replace(/[<>]/g, '');
+const prompt = `Here's a blog post:\n\n${sanitizedContent}\n\nQuestion: ${sanitizedQuery}`;

Likely invalid or redundant comment.

🧰 Tools
🪛 eslint

[error] 33-33: Delete ;

(prettier/prettier)


[error] 35-35: Delete ;

(prettier/prettier)

src/components/BlogPostClient.tsx (1)

67-72: LGTM! Clean integration of BlogChatInterface.

The component integration is well-structured with appropriate props passing and separation of concerns.

🧰 Tools
🪛 eslint

[error] 67-67: Delete ········

(prettier/prettier)


[error] 68-68: Replace ···················· with ··········

(prettier/prettier)


[error] 69-69: Replace ···················· with ··········

(prettier/prettier)


[error] 70-70: Replace ···················· with ··········

(prettier/prettier)


[error] 71-71: Replace ···················· with ··········

(prettier/prettier)


[error] 72-72: Delete ········

(prettier/prettier)

tailwind.config.ts (1)

104-108: LGTM! Well-structured animation definitions.

The animations are well-defined with consistent timing functions and durations.

🧰 Tools
🪛 eslint

[error] 104-104: Replace 'fade-in-down':·'fade-in-down·0.5s·ease-out' with "fade-in-down":·"fade-in-down·0.5s·ease-out"

(prettier/prettier)


[error] 106-106: Replace 'fade-in-up':·'fade-in-up·0.5s·ease-out' with "fade-in-up":·"fade-in-up·0.5s·ease-out"

(prettier/prettier)


[error] 107-107: Replace 'fade-in':·'fade-in·0.5s·ease-out' with "fade-in":·"fade-in·0.5s·ease-out"

(prettier/prettier)


[error] 108-108: Replace 'pulse-shadow':·'pulse-shadow·2s·infinite' with "pulse-shadow":·"pulse-shadow·2s·infinite",

(prettier/prettier)

src/components/BlogChatInterface.tsx (2)

1-19: LGTM! Well-structured interfaces and imports.

The interfaces are properly typed and the props structure aligns well with the component's requirements.

🧰 Tools
🪛 eslint

[error] 1-1: Replace 'react'; with "react"

(prettier/prettier)


[error] 2-2: Replace 'framer-motion'; with "framer-motion"

(prettier/prettier)


[error] 3-3: Replace 'lucide-react'; with "lucide-react"

(prettier/prettier)


[error] 4-4: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 5-5: Replace './ChatSuggestions'; with "./ChatSuggestions"

(prettier/prettier)


[error] 6-6: Replace 'dompurify'; with "dompurify"

(prettier/prettier)


[error] 7-7: Delete ;

(prettier/prettier)


[error] 8-8: Replace '@/hooks/use-toast'; with "@/hooks/use-toast"

(prettier/prettier)


[error] 16-16: Replace ····blogContent:·string; with ··blogContent:·string

(prettier/prettier)


[error] 17-17: Replace ····blogTitle:·string; with ··blogTitle:·string

(prettier/prettier)


[error] 18-18: Replace ····blogDescription:·string; with ··blogDescription:·string

(prettier/prettier)


[error] 19-19: Replace ····onClose:·()·=>·void; with ··onClose:·()·=>·void

(prettier/prettier)


124-132: ⚠️ Potential issue

Critical: Add sanitization for rendered HTML content

The assistant messages are rendered as HTML without sanitization at render time, which could lead to XSS vulnerabilities if the content was modified after the initial sanitization.

Apply this diff to ensure content is always sanitized at render time:

 {msg.role === 'human' ? (
     <>
         {msg.content}
     </>
 ) : (
     <>
-        {parse(msg.content)}
+        {parse(DOMPurify.sanitize(msg.content))}
     </>
 )}

Likely invalid or redundant comment.

🧰 Tools
🪛 eslint

[error] 124-124: Replace ············································{msg.role·===·'human' with ························{msg.role·===·"human"

(prettier/prettier)


[error] 125-127: Replace ················································<>⏎····················································{msg.content}⏎················································ with ··························<>{msg.content}

(prettier/prettier)


[error] 128-128: Replace ············································ with ························

(prettier/prettier)


[error] 129-131: Replace ················································<>⏎····················································{parse(msg.content)}⏎················································ with ··························<>{parse(msg.content)}

(prettier/prettier)


[error] 132-132: Replace ············································ with ························

(prettier/prettier)

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: 2

🧹 Nitpick comments (7)
src/app/api/summarize/route.ts (3)

17-21: Improve system instruction formatting and clarity

The system instruction could be more concise and better formatted.

-    systemInstruction: 
-      "You're an AI assistant, Who only answers questions related to the blog post that have provided to you in the prompt.\n\n" +
-      "Try to Answer using numeric points for readability and Bold the title of the point.\n\n" +
-      "If the user wants help related to the topic mentioned in the blog you can answer that.\n\n" +
-      "If the question is not related to the blog post or the topic that is mentioned on the blog, Throw error response."
+    systemInstruction: [
+      "You are a blog-focused AI assistant that:",
+      "1. Only answers questions related to the provided blog post",
+      "2. Uses numbered points with bold titles for readability",
+      "3. Provides help related to the blog's topic",
+      "4. Returns an error for off-topic questions"
+    ].join("\n")
🧰 Tools
🪛 eslint

[error] 17-17: Delete ·

(prettier/prettier)


[error] 21-21: Insert ,

(prettier/prettier)


31-36: Add content length validation

Consider adding length validation to prevent extremely long inputs that could impact performance or exceed API limits.

-  if (!body.content || typeof body.content !== 'string' || !body.query || typeof body.query !== 'string') {
+  const MAX_CONTENT_LENGTH = 10000; // Adjust based on Gemini API limits
+  const MAX_QUERY_LENGTH = 1000;
+  
+  if (!body.content || typeof body.content !== 'string' || 
+      !body.query || typeof body.query !== 'string' ||
+      body.content.length > MAX_CONTENT_LENGTH ||
+      body.query.length > MAX_QUERY_LENGTH) {
     return NextResponse.json(
-      { error: 'Invalid request: content and query are required and must be strings', success: false },
+      { 
+        error: 'Invalid request: content and query must be strings within length limits',
+        success: false,
+        limits: { content: MAX_CONTENT_LENGTH, query: MAX_QUERY_LENGTH }
+      },
       { status: 400 }
     );
   }
🧰 Tools
🪛 eslint

[error] 31-31: Replace !body.content·||·typeof·body.content·!==·'string'·||·!body.query·||·typeof·body.query·!==·'string' with ⏎····!body.content·||⏎····typeof·body.content·!==·"string"·||⏎····!body.query·||⏎····typeof·body.query·!==·"string"⏎··

(prettier/prettier)


[error] 33-33: Replace ·error:·'Invalid·request:·content·and·query·are·required·and·must·be·strings',·success:·false with ⏎········error:⏎··········"Invalid·request:·content·and·query·are·required·and·must·be·strings",⏎········success:·false,⏎·····

(prettier/prettier)


[error] 35-35: Delete ;

(prettier/prettier)


51-55: Enhance error logging structure

Consider using a more structured logging format to facilitate error tracking and analysis.

-    console.error('API Error:', {
-      type: isGoogleAIError ? 'GoogleGenerativeAI' : 'Unknown',
-      message: error.message,
-      timestamp: new Date().toISOString()
-    });
+    console.error(JSON.stringify({
+      level: 'error',
+      service: 'summarize-api',
+      errorType: isGoogleAIError ? 'GoogleGenerativeAI' : 'Unknown',
+      errorMessage: error.message,
+      timestamp: new Date().toISOString(),
+      requestId: crypto.randomUUID(), // Add request tracking
+      stack: error instanceof Error ? error.stack : undefined
+    }));
🧰 Tools
🪛 eslint

[error] 51-51: Replace 'API·Error:' with "API·Error:"

(prettier/prettier)


[error] 52-52: Replace 'GoogleGenerativeAI'·:·'Unknown' with "GoogleGenerativeAI"·:·"Unknown"

(prettier/prettier)


[error] 54-54: Insert ,

(prettier/prettier)


[error] 55-55: Delete ;

(prettier/prettier)

src/components/BlogChatInterface.tsx (4)

11-12: Enhance ChatMessage interface with additional properties

Consider adding more properties to make the ChatMessage interface more robust and future-proof.

 interface ChatMessage {
-    role: "human" | "assistant"
-    content: string
+    role: "human" | "assistant";
+    content: string;
+    timestamp: string;
+    id: string;
+    status?: "sending" | "sent" | "error";
+    error?: string;
 }
🧰 Tools
🪛 eslint

[error] 11-11: Delete ··

(prettier/prettier)


[error] 12-12: Replace ···· with ··

(prettier/prettier)


53-58: Add retry logic for transient failures

Consider implementing retry logic for transient API failures to improve reliability.

+    const fetchWithRetry = async (url: string, options: RequestInit, maxRetries = 3) => {
+      for (let i = 0; i < maxRetries; i++) {
+        try {
+          const response = await fetch(url, options);
+          if (response.status !== 503) return response; // Only retry on service unavailable
+          await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 1000));
+        } catch (error) {
+          if (i === maxRetries - 1) throw error;
+        }
+      }
+    };
+
-            const response = await fetch('/api/summarize', {
+            const response = await fetchWithRetry('/api/summarize', {
                 method: 'POST',
                 headers: { 'Content-Type': 'application/json' },
                 body: JSON.stringify({ content: blogContent, query: promptToSend }),
             });
🧰 Tools
🪛 eslint

[error] 53-53: Replace ········ with ····

(prettier/prettier)


[error] 54-54: Replace ············const·response·=·await·fetch('/api/summarize' with ······const·response·=·await·fetch("/api/summarize"

(prettier/prettier)


[error] 55-55: Replace ················method:·'POST' with ········method:·"POST"

(prettier/prettier)


[error] 56-56: Replace ················headers:·{·'Content-Type':·'application/json' with ········headers:·{·"Content-Type":·"application/json"

(prettier/prettier)


[error] 57-57: Delete ········

(prettier/prettier)


128-136: Improve message accessibility

Add ARIA labels and roles to improve accessibility for screen readers.

     {msg.role === 'human' ? (
-        <>
+        <div role="log" aria-label="User message">
             {msg.content}
-        </>
+        </div>
     ) : (
-        <>
+        <div role="log" aria-label="Assistant response">
             {parse(msg.content)}
-        </>
+        </div>
     )}
🧰 Tools
🪛 eslint

[error] 128-128: Replace ············································{msg.role·===·'human' with ························{msg.role·===·"human"

(prettier/prettier)


[error] 129-131: Replace ················································<>⏎····················································{msg.content}⏎················································ with ··························<>{msg.content}

(prettier/prettier)


[error] 132-132: Replace ············································ with ························

(prettier/prettier)


[error] 133-135: Replace ················································<>⏎····················································{parse(msg.content)}⏎················································ with ··························<>{parse(msg.content)}

(prettier/prettier)


[error] 136-136: Replace ············································ with ························

(prettier/prettier)


201-213: Enhance markdown parsing capabilities

Consider supporting more markdown features for richer content formatting.

 const parseContent = (content: string) => {
-    const formattedContent = content.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
+    const formattedContent = content
+        .replace(/\*\*(.*?)\*\*/g, '<b>$1</b>')
+        .replace(/\*(.*?)\*/g, '<em>$1</em>')
+        .replace(/`(.*?)`/g, '<code>$1</code>')
+        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');

     const paragraphWrapped = formattedContent
         .split('\n\n')
         .map((line) => `<p>${line.trim()}</p>`)
         .join('');

     return DOMPurify.sanitize(paragraphWrapped);
 };
🧰 Tools
🪛 eslint

[error] 202-203: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>');⏎ with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 205-205: Delete ··

(prettier/prettier)


[error] 206-206: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 207-207: Delete ····

(prettier/prettier)


[error] 208-208: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 210-212: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 421f2ac and 796158f.

📒 Files selected for processing (3)
  • .vscode/settings.json (1 hunks)
  • src/app/api/summarize/route.ts (1 hunks)
  • src/components/BlogChatInterface.tsx (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/components/BlogChatInterface.tsx (1)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/BlogChatInterface.tsx:37-42
Timestamp: 2024-12-06T20:38:27.090Z
Learning: In the `src/components/BlogChatInterface.tsx` file of our React application, the `toast` function supports the properties `color`, `duration`, and `style`, allowing customization of toast notifications.
🪛 eslint
src/app/api/summarize/route.ts

[error] 4-4: Delete ;

(prettier/prettier)


[error] 6-7: Replace ⏎const·GEMINI_API_KEY·=·process.env.GEMINI_API_KEY; with const·GEMINI_API_KEY·=·process.env.GEMINI_API_KEY

(prettier/prettier)


[error] 12-12: Replace GenerativeModel; with ·GenerativeModel

(prettier/prettier)


[error] 14-14: Delete ;

(prettier/prettier)


[error] 15-15: Delete ··

(prettier/prettier)


[error] 17-17: Delete ·

(prettier/prettier)


[error] 21-21: Insert ,

(prettier/prettier)


[error] 22-22: Delete ;

(prettier/prettier)


[error] 24-24: Replace 'Failed·to·initialize·Gemini·model:',·error); with "Failed·to·initialize·Gemini·model:",·error)

(prettier/prettier)


[error] 25-25: Replace 'Failed·to·initialize·AI·service.·Please·try·again·later.'); with "Failed·to·initialize·AI·service.·Please·try·again·later.")

(prettier/prettier)


[error] 29-29: Delete ;

(prettier/prettier)


[error] 31-31: Replace !body.content·||·typeof·body.content·!==·'string'·||·!body.query·||·typeof·body.query·!==·'string' with ⏎····!body.content·||⏎····typeof·body.content·!==·"string"·||⏎····!body.query·||⏎····typeof·body.query·!==·"string"⏎··

(prettier/prettier)


[error] 33-33: Replace ·error:·'Invalid·request:·content·and·query·are·required·and·must·be·strings',·success:·false with ⏎········error:⏎··········"Invalid·request:·content·and·query·are·required·and·must·be·strings",⏎········success:·false,⏎·····

(prettier/prettier)


[error] 35-35: Delete ;

(prettier/prettier)


[error] 38-38: Delete ;

(prettier/prettier)


[error] 41-41: Delete ;

(prettier/prettier)


[error] 43-43: Delete ;

(prettier/prettier)


[error] 47-48: Delete ⏎·

(prettier/prettier)


[error] 49-49: Delete ;

(prettier/prettier)


[error] 51-51: Replace 'API·Error:' with "API·Error:"

(prettier/prettier)


[error] 52-52: Replace 'GoogleGenerativeAI'·:·'Unknown' with "GoogleGenerativeAI"·:·"Unknown"

(prettier/prettier)


[error] 54-54: Insert ,

(prettier/prettier)


[error] 55-55: Delete ;

(prettier/prettier)


[error] 58-58: Replace 'The·AI·service·is·temporarily·unavailable.·Please·try·again·later.' with "The·AI·service·is·temporarily·unavailable.·Please·try·again·later."

(prettier/prettier)


[error] 59-59: Replace 'Internal·Server·Error.·Please·try·again·after·some·time.'; with "Internal·Server·Error.·Please·try·again·after·some·time."

(prettier/prettier)


[error] 63-63: Insert ,

(prettier/prettier)


[error] 66-66: Delete ;

(prettier/prettier)

src/components/BlogChatInterface.tsx

[error] 1-1: Replace 'react'; with "react"

(prettier/prettier)


[error] 2-2: Replace 'framer-motion'; with "framer-motion"

(prettier/prettier)


[error] 3-3: Replace 'lucide-react'; with "lucide-react"

(prettier/prettier)


[error] 4-4: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 5-5: Replace './ChatSuggestions'; with "./ChatSuggestions"

(prettier/prettier)


[error] 6-6: Replace 'dompurify'; with "dompurify"

(prettier/prettier)


[error] 7-7: Delete ;

(prettier/prettier)


[error] 8-8: Replace '@/hooks/use-toast'; with "@/hooks/use-toast"

(prettier/prettier)


[error] 11-11: Delete ··

(prettier/prettier)


[error] 12-12: Replace ···· with ··

(prettier/prettier)


[error] 16-16: Replace ··blogContent:·string; with blogContent:·string

(prettier/prettier)


[error] 17-17: Replace ····blogTitle:·string; with ··blogTitle:·string

(prettier/prettier)


[error] 18-18: Replace ····blogDescription:·string; with ··blogDescription:·string

(prettier/prettier)


[error] 19-19: Replace ··onClose:·()·=>·void; with onClose:·()·=>·void

(prettier/prettier)


[error] 22-22: Replace ·blogContent,·blogTitle,·blogDescription,·onClose· with ⏎··blogContent,⏎··blogTitle,⏎··blogDescription,⏎··onClose,⏎

(prettier/prettier)


[error] 23-23: Replace ····const·[messages,·setMessages]·=·useState<ChatMessage[]>([]); with ··const·[messages,·setMessages]·=·useState<ChatMessage[]>([])

(prettier/prettier)


[error] 24-24: Replace ····const·[input,·setInput]·=·useState(''); with ··const·[input,·setInput]·=·useState("")

(prettier/prettier)


[error] 25-25: Replace ····const·[isLoading,·setIsLoading]·=·useState(false); with ··const·[isLoading,·setIsLoading]·=·useState(false)

(prettier/prettier)


[error] 26-26: Replace ····const·messagesEndRef·=·useRef<HTMLDivElement>(null); with ··const·messagesEndRef·=·useRef<HTMLDivElement>(null)

(prettier/prettier)


[error] 27-27: Replace ····const·{·toast·}·=·useToast(); with ··const·{·toast·}·=·useToast()

(prettier/prettier)


[error] 28-28: Replace ···· with ··

(prettier/prettier)


[error] 29-29: Replace ········messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·}); with ····messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·})

(prettier/prettier)


[error] 30-30: Replace ····}; with ··}

(prettier/prettier)


[error] 32-32: Replace ···· with ··

(prettier/prettier)


[error] 34-34: Delete ··

(prettier/prettier)


[error] 35-35: Replace ········const·promptToSend·=·customPrompt·||·input; with ····const·promptToSend·=·customPrompt·||·input

(prettier/prettier)


[error] 36-36: Replace ········ with ····

(prettier/prettier)


[error] 37-37: Replace ············ with ······

(prettier/prettier)


[error] 38-38: Replace ················variant:·'destructive' with ········variant:·"destructive"

(prettier/prettier)


[error] 39-39: Replace ················color:·'gray' with ········color:·"gray"

(prettier/prettier)


[error] 40-40: Replace ················ with ········

(prettier/prettier)


[error] 41-41: Replace ········style:·{·background:·'#7f1d1d',·color:·'white',·border:·'gray',·borderRadius:·'5px'·} with style:·{⏎··········background:·"#7f1d1d",⏎··········color:·"white",⏎··········border:·"gray",⏎··········borderRadius:·"5px"

(prettier/prettier)


[error] 42-42: Replace ········description:·"Please·enter·valid·input." with },⏎········description:·"Please·enter·valid·input.",

(prettier/prettier)


[error] 43-43: Replace ············ with ······

(prettier/prettier)


[error] 45-45: Replace ············return; with ······return

(prettier/prettier)


[error] 46-46: Replace ········}; with ····}

(prettier/prettier)


[error] 48-48: Replace ········const·userMessage:·ChatMessage·=·{·role:·'human',·content:·promptToSend·}; with ····const·userMessage:·ChatMessage·=·{·role:·"human",·content:·promptToSend·}

(prettier/prettier)


[error] 49-49: Replace ········setMessages(prev·=>·[...prev,·userMessage]); with ····setMessages((prev)·=>·[...prev,·userMessage])

(prettier/prettier)


[error] 50-50: Replace ········setInput(''); with ····setInput("")

(prettier/prettier)


[error] 51-51: Replace ········setIsLoading(true); with ····setIsLoading(true)

(prettier/prettier)


[error] 53-53: Replace ········ with ····

(prettier/prettier)


[error] 54-54: Replace ············const·response·=·await·fetch('/api/summarize' with ······const·response·=·await·fetch("/api/summarize"

(prettier/prettier)


[error] 55-55: Replace ················method:·'POST' with ········method:·"POST"

(prettier/prettier)


[error] 56-56: Replace ················headers:·{·'Content-Type':·'application/json' with ········headers:·{·"Content-Type":·"application/json"

(prettier/prettier)


[error] 57-57: Delete ········

(prettier/prettier)


[error] 58-59: Replace ······});⏎ with })

(prettier/prettier)


[error] 61-62: Delete ⏎······

(prettier/prettier)


[error] 63-63: Replace ················const·data·=·await·response.json(); with ········const·data·=·await·response.json()

(prettier/prettier)


[error] 64-64: Replace ················setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·"Error:·"·+·data.error·}]); with ········setMessages((prev)·=>·[⏎··········...prev,⏎··········{·role:·"assistant",·content:·"Error:·"·+·data.error·},

(prettier/prettier)


[error] 65-65: Replace ········return; with ])⏎········return

(prettier/prettier)


[error] 66-67: Replace ⏎············ with ······

(prettier/prettier)


[error] 68-68: Replace ············const·data·=·await·response.json(); with ······const·data·=·await·response.json()

(prettier/prettier)


[error] 70-70: Replace ············const·responseAnswer·=·parseContent(data.answer); with ······const·responseAnswer·=·parseContent(data.answer)

(prettier/prettier)


[error] 72-72: Replace ······setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·responseAnswer·}]); with setMessages((prev)·=>·[⏎········...prev,⏎········{·role:·"assistant",·content:·responseAnswer·},

(prettier/prettier)


[error] 73-73: Replace ···· with ······])⏎

(prettier/prettier)


[error] 74-74: Replace ············console.error('Error·in·chat:',·error); with ······console.error("Error·in·chat:",·error)

(prettier/prettier)


[error] 75-75: Replace ············const·errorMessage·= with ······const·errorMessage·=⏎·······

(prettier/prettier)


[error] 76-76: Replace ················ with ··········

(prettier/prettier)


[error] 77-77: Replace ················:·"An·unexpected·error·occurred.·Please·try·again."; with ··········:·"An·unexpected·error·occurred.·Please·try·again."

(prettier/prettier)


[error] 78-78: Replace ············setMessages(prev·=>·[...prev,·{ with ······setMessages((prev)·=>·[⏎········...prev,

(prettier/prettier)


[error] 79-79: Replace ········role:·'assistant' with {⏎··········role:·"assistant"

(prettier/prettier)


[error] 80-80: Replace ················content:·errorMessage with ··········content:·errorMessage,

(prettier/prettier)


[error] 81-81: Replace ············}]) with ········},

(prettier/prettier)


[error] 82-82: Replace ··} with ])

(prettier/prettier)


[error] 83-83: Replace ······· with ····}

(prettier/prettier)


[error] 84-84: Replace ············setIsLoading(false); with ······setIsLoading(false)

(prettier/prettier)


[error] 85-85: Replace ········ with ····

(prettier/prettier)


[error] 86-86: Replace ····}; with ··}

(prettier/prettier)


[error] 87-87: Delete ··

(prettier/prettier)


[error] 88-88: Replace ········ with ····

(prettier/prettier)


[error] 89-89: Delete ··

(prettier/prettier)


[error] 90-90: Delete ··

(prettier/prettier)


[error] 91-91: Delete ····

(prettier/prettier)


[error] 92-92: Delete ······

(prettier/prettier)


[error] 93-93: Replace ············ with ······

(prettier/prettier)


[error] 94-94: Replace ············ with ······

(prettier/prettier)


[error] 95-95: Delete ······

(prettier/prettier)


[error] 96-96: Delete ····

(prettier/prettier)


[error] 97-97: Replace ············ with ······

(prettier/prettier)


[error] 98-98: Delete ········

(prettier/prettier)


[error] 99-99: Delete ········

(prettier/prettier)


[error] 100-100: Replace ················ with ········

(prettier/prettier)


[error] 101-101: Delete ······

(prettier/prettier)


[error] 102-102: Replace ················ with ········

(prettier/prettier)


[error] 103-103: Replace ····················<h2·className="text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">Chat·about·the·Blog·<ChatBubbleLeftIcon·className='w-7·h-7'></ChatBubbleLeftIcon></h2> with ··········<h2·className="text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">⏎············Chat·about·the·Blog{"·"}⏎············<ChatBubbleLeftIcon·className="w-7·h-7"></ChatBubbleLeftIcon>

(prettier/prettier)


[error] 104-104: Insert </h2>⏎

(prettier/prettier)


[error] 105-105: Delete ············

(prettier/prettier)


[error] 106-106: Replace ···················· with ··········

(prettier/prettier)


[error] 107-107: Replace ················ with ········

(prettier/prettier)


[error] 108-108: Delete ········

(prettier/prettier)


[error] 109-109: Delete ··········

(prettier/prettier)


[error] 110-110: Replace ························{messages.length·==·0·?·<ChatSuggestions·title={blogTitle}·description={blogDescription}·handleSend={handleSend}></ChatSuggestions·>·: with ············{messages.length·==·0·?·(⏎··············<ChatSuggestions⏎················title={blogTitle}⏎················description={blogDescription}⏎················handleSend={handleSend}⏎··············></ChatSuggestions>⏎············)·:·(⏎·············

(prettier/prettier)


[error] 111-111: Replace ····························<motion.div·key={index} with ················<motion.div

(prettier/prettier)


[error] 112-112: Insert ····key={index}⏎

(prettier/prettier)


[error] 113-113: Replace ································ with ··················

(prettier/prettier)


[error] 114-114: Replace ································exit={{·opacity:·0,·y:·-20·}}·className='flex·flex-col··gap-4·'> with ··················exit={{·opacity:·0,·y:·-20·}}⏎··················className="flex·flex-col··gap-4·"

(prettier/prettier)


[error] 115-115: Replace ·················<div·className='flex·gap-4·items-start' with ·>⏎··················<div·className="flex·gap-4·items-start"

(prettier/prettier)


[error] 116-116: Replace ················<div·className='min-w-12·min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·rounded-full·bg-black' with <div·className="min-w-12·min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·rounded-full·bg-black"

(prettier/prettier)


[error] 117-118: Replace ········································{msg.role·===·"human"·?·<User></User>·:·<BotIcon></BotIcon>}⏎····································</div with ······················{msg.role·===·"human"·?·(⏎························<User></User>⏎······················)·:·(⏎························<BotIcon></BotIcon

(prettier/prettier)


[error] 119-119: Replace ·················<motion.div with ···)}⏎····················</div>

(prettier/prettier)


[error] 120-120: Insert ··<motion.div⏎

(prettier/prettier)


[error] 121-121: Replace ········································ with ······················

(prettier/prettier)


[error] 122-122: Replace ········································ with ······················

(prettier/prettier)


[error] 123-123: Replace ········································className={flex·${msg.role·===·'human'·?·'justify-end'·:·'justify-start'with······················className={flex·${msg.role·===·"human"·?·"justify-end"·:·"justify-start"

(prettier/prettier)


[error] 124-124: Replace ···································· with ····················

(prettier/prettier)


[error] 125-125: Replace ·······················<div·className={max-w-full·text-[16px]·leading-relaxed·tracking-wide·shadow-lg·shadow-black·px-5·py-4·rounded-xl·${msg.role·===·'human'·?·'bg-white/80·text-black/80'·:·'bg-[#09090b]·text-gray-200'with·····<div⏎························className={max-w-full·text-[16px]·leading-relaxed·tracking-wide·shadow-lg·shadow-black·px-5·py-4·rounded-xl·${⏎··························msg.role·===·"human"⏎····························?·"bg-white/80·text-black/80"⏎····························:·"bg-[#09090b]·text-gray-200"

(prettier/prettier)


[error] 126-126: Replace ············································ with ························

(prettier/prettier)


[error] 127-127: Replace ········································ with ······················

(prettier/prettier)


[error] 128-128: Replace ············································{msg.role·===·'human' with ························{msg.role·===·"human"

(prettier/prettier)


[error] 129-131: Replace ················································<>⏎····················································{msg.content}⏎················································ with ··························<>{msg.content}

(prettier/prettier)


[error] 132-132: Replace ············································ with ························

(prettier/prettier)


[error] 133-135: Replace ················································<>⏎····················································{parse(msg.content)}⏎················································ with ··························<>{parse(msg.content)}

(prettier/prettier)


[error] 136-136: Replace ············································ with ························

(prettier/prettier)


[error] 137-137: Replace ········································ with ······················

(prettier/prettier)


[error] 138-138: Replace ···································· with ····················

(prettier/prettier)


[error] 139-139: Replace ································ with ··················

(prettier/prettier)


[error] 140-140: Replace ··············<div·className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}></div> with <div⏎····················className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}

(prettier/prettier)


[error] 141-141: Replace ············ with ··················></div>⏎

(prettier/prettier)


[error] 142-142: Replace ··········) with ))⏎············

(prettier/prettier)


[error] 143-143: Delete ··········

(prettier/prettier)


[error] 144-144: Replace ···················· with ··········

(prettier/prettier)


[error] 145-145: Replace ························ with ············

(prettier/prettier)


[error] 146-146: Replace ···························· with ··············

(prettier/prettier)


[error] 147-147: Replace ···························· with ··············

(prettier/prettier)


[error] 148-148: Delete ··············

(prettier/prettier)


[error] 149-149: Delete ············

(prettier/prettier)


[error] 150-150: Replace ····························<div·className='min-w-12·min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·bg-black' with ··············<div·className="min-w-12·min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·bg-black"

(prettier/prettier)


[error] 151-151: Delete ················

(prettier/prettier)


[error] 152-152: Replace ···························· with ··············

(prettier/prettier)


[error] 153-153: Delete ··············

(prettier/prettier)


[error] 154-155: Delete ⏎················

(prettier/prettier)


[error] 156-156: Replace ································ with ················

(prettier/prettier)


[error] 157-157: Replace ···························· with ··············

(prettier/prettier)


[error] 158-158: Delete ············

(prettier/prettier)


[error] 159-159: Replace ···················· with ··········

(prettier/prettier)


[error] 160-160: Delete ··········

(prettier/prettier)


[error] 161-161: Replace ················ with ········

(prettier/prettier)


[error] 162-162: Delete ········

(prettier/prettier)


[error] 163-163: Replace ···················· with ··········

(prettier/prettier)


[error] 164-164: Delete ············

(prettier/prettier)


[error] 164-174: The element input has an implicit role of textbox. Defining this explicitly is redundant and should be avoided.

(jsx-a11y/no-redundant-roles)


[error] 165-165: Replace ···························· with ··············

(prettier/prettier)


[error] 166-166: Replace ···························· with ··············

(prettier/prettier)


[error] 167-167: Replace ···························· with ··············

(prettier/prettier)


[error] 168-168: Delete ··············

(prettier/prettier)


[error] 169-169: Replace ···························· with ··············

(prettier/prettier)


[error] 170-170: Replace ···························· with ··············

(prettier/prettier)


[error] 171-171: Replace ····························onKeyPress={(e)·=>·e.key·===·'Enter' with ··············onKeyPress={(e)·=>·e.key·===·"Enter"

(prettier/prettier)


[error] 172-172: Delete ··············

(prettier/prettier)


[error] 173-173: Delete ··············

(prettier/prettier)


[error] 174-174: Delete ············

(prettier/prettier)


[error] 175-175: Replace ············<button·onClick={handleSummary}·className='bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black'>Summarize with <button⏎··············onClick={handleSummary}⏎··············className="bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black"⏎············>⏎··············Summarize{"·"}⏎···········

(prettier/prettier)


[error] 177-177: Replace ············<button with <button⏎·············

(prettier/prettier)


[error] 178-178: Replace ··············disabled={isLoading}·className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"> with disabled={isLoading}⏎··············className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"

(prettier/prettier)


[error] 179-179: Replace ·· with >⏎

(prettier/prettier)


[error] 180-180: Replace ································ with ················

(prettier/prettier)


[error] 181-181: Replace ···························· with ··············

(prettier/prettier)


[error] 182-182: Delete ··············

(prettier/prettier)


[error] 183-183: Delete ················

(prettier/prettier)


[error] 184-184: Delete ··················

(prettier/prettier)


[error] 185-186: Replace ⏎································ with ················

(prettier/prettier)


[error] 187-188: Replace ⏎···························· with ··············

(prettier/prettier)


[error] 189-189: Delete ··············

(prettier/prettier)


[error] 190-191: Replace ············</button>⏎ with </button>

(prettier/prettier)


[error] 192-192: Delete ··········

(prettier/prettier)


[error] 193-193: Delete ········

(prettier/prettier)


[error] 194-194: Replace ············ with ······

(prettier/prettier)


[error] 195-195: Replace ········ with ····

(prettier/prettier)


[error] 196-196: Replace ····); with ··)

(prettier/prettier)


[error] 197-199: Delete ;⏎⏎

(prettier/prettier)


[error] 202-203: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>');⏎ with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 205-205: Delete ··

(prettier/prettier)


[error] 206-206: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 207-207: Delete ····

(prettier/prettier)


[error] 208-208: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 210-212: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)


[error] 213-217: Delete ;⏎⏎⏎⏎

(prettier/prettier)


[error] 219-219: Replace ; with

(prettier/prettier)

🔇 Additional comments (1)
src/app/api/summarize/route.ts (1)

41-46: ⚠️ Potential issue

Sanitize inputs to prevent prompt injection

Direct concatenation of user input into the prompt could lead to prompt injection attacks.

+  const sanitizeInput = (input: string): string => {
+    return input
+      .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') // Escape markdown characters
+      .replace(/\n{3,}/g, '\n\n') // Normalize multiple newlines
+      .trim();
+  };
+
-    const prompt = `Here's a blog post:\n\n${content}\n\nQuestion: ${query}`;
+    const prompt = `Here's a blog post:\n\n${sanitizeInput(content)}\n\nQuestion: ${sanitizeInput(query)}`;

Likely invalid or redundant comment.

🧰 Tools
🪛 eslint

[error] 41-41: Delete ;

(prettier/prettier)


[error] 43-43: Delete ;

(prettier/prettier)

@Rahulsoni9321
Copy link
Author

soni9

Hey @nirnejak ,
Resolved all conflicts and made all the changes suggested by coderabbit. Please have a look and let me know if anything else is needed.

@Rahulsoni9321
Copy link
Author

Hey @nirnejak resolved all the conflicts and made the chat interface responsive. Please let me know if anything else is needed.

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 (10)
tailwind.config.ts (2)

89-96: Consider using CSS variables for animation colors

The pulse-shadow animation uses hardcoded rgba values. Consider using CSS variables for consistency with the rest of the theme system.

  "pulse-shadow": {
    "0%, 100%": {
-     boxShadow: "0 0 0 0px rgba(147, 51, 234, 0.7)",
+     boxShadow: "0 0 0 0px hsl(var(--primary) / 0.7)",
    },
    "50%": {
-     boxShadow: "0 0 0 10px rgba(147, 51, 234, 0)",
+     boxShadow: "0 0 0 10px hsl(var(--primary) / 0)",
    },
  },

105-142: Remove unused theme parameter

The theme parameter in the typography configuration is not being used.

- typography: (theme: any) => ({
+ typography: {
🧰 Tools
🪛 eslint

[error] 105-105105: 'theme' is defined but never used.

(no-unused-vars)

src/components/ChatSuggestions.tsx (2)

19-20: Suggestion text grammar.

Currently, the text says: “Make me understand this blog : {title}?”. The sentence structure works, but consider rewording for clarity, for example: “Help me understand this blog: {title}?”. Also, remove extra spacing around the colon if it’s unintentional.

- handleSend(`Make me understand this blog : ${title}?`)
+ handleSend(`Help me understand this blog: ${title}?`)
🧰 Tools
🪛 eslint

[error] 19-1919: Replace ················<motion.div·onClick={()·=>·{·handleSend(Make·me·understand·this·blog·:·${title}?)·}}·initial={{·y:·20,·opacity:·0·}}·whileHover={{·scale:·1.05·}}·animate={{·y:·0,·opacity:·1·}}·transition={{·duration:·0.1·}}·exit={{·y:·20,·opacity:·0·}}·className='px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer'> with ········<motion.div⏎··········onClick={()·=>·{⏎············handleSend(Make·me·understand·this·blog·:·${title}?)⏎··········}}⏎··········initial={{·y:·20,·opacity:·0·}}⏎··········whileHover={{·scale:·1.05·}}⏎··········animate={{·y:·0,·opacity:·1·}}⏎··········transition={{·duration:·0.1·}}⏎··········exit={{·y:·20,·opacity:·0·}}⏎··········className="px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer"

(prettier/prettier)


[error] 20-2020: Replace ·· with >⏎

(prettier/prettier)


22-23: Minor grammatical fix in text.

"What does "${description}" means in this blog?" can be changed to "What does "${description}" mean in this blog?" to correct the subject-verb agreement.

- handleSend(`What does "${description}" means in this blog? `)
+ handleSend(`What does "${description}" mean in this blog?`)
🧰 Tools
🪛 eslint

[error] 22-2222: Replace ················<motion.div·onClick={()·=>·{·handleSend(What·does·"${description}"·means·in·this·blog?·)·}}·initial={{·y:·20,·opacity:·0·}}·whileHover={{·scale:·1.05·}}·animate={{·y:·0,·opacity:·1·}}·transition={{·duration:·0.1·}}·exit={{·y:·20,·opacity:·0·}}·className='px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer'> with ········<motion.div⏎··········onClick={()·=>·{⏎············handleSend(What·does·"${description}"·means·in·this·blog?·)⏎··········}}⏎··········initial={{·y:·20,·opacity:·0·}}⏎··········whileHover={{·scale:·1.05·}}⏎··········animate={{·y:·0,·opacity:·1·}}⏎··········transition={{·duration:·0.1·}}⏎··········exit={{·y:·20,·opacity:·0·}}⏎··········className="px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer"

(prettier/prettier)


[error] 23-2323: Replace ·· with >⏎

(prettier/prettier)

src/components/BlogPostClient.tsx (1)

56-58: Launch Chat button improvements.

The “Summarize with AI” label is clear. The shimmer effect is visually engaging. Consider verifying color contrast and potential motion preferences for users who disable animations.

🧰 Tools
🪛 eslint

[error] 56-5656: Replace ··········<button·onClick={()·=>·setShowChat(true)}·className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"> with <button⏎············onClick={()·=>·setShowChat(true)}⏎············className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"

(prettier/prettier)


[error] 57-5757: Replace ·· with >⏎

(prettier/prettier)


[error] 58-5858: Replace ···················· with ··········

(prettier/prettier)

src/components/BlogChatInterface.tsx (4)

11-12: Consider enhancing the ChatMessage interface.

The ChatMessage interface could be more specific about the content types it handles. Consider using discriminated unions to differentiate between human and assistant message structures.

-interface ChatMessage {
-    role: "human" | "assistant"
-    content: string
-}
+interface BaseMessage {
+    role: "human" | "assistant"
+}
+
+interface HumanMessage extends BaseMessage {
+    role: "human"
+    content: string
+}
+
+interface AssistantMessage extends BaseMessage {
+    role: "assistant"
+    content: string
+    error?: string
+}
+
+type ChatMessage = HumanMessage | AssistantMessage
🧰 Tools
🪛 eslint

[error] 11-1111: Delete ··

(prettier/prettier)


[error] 12-1212: Replace ···· with ··

(prettier/prettier)


54-58: Add timeout handling to the fetch call.

The API call could hang indefinitely without proper timeout handling. Consider adding a timeout to prevent poor user experience.

-const response = await fetch('/api/summarize', {
+const controller = new AbortController();
+const timeout = setTimeout(() => controller.abort(), 30000);
+const response = await fetch('/api/summarize', {
     method: 'POST',
     headers: { 'Content-Type': 'application/json' },
     body: JSON.stringify({ content: blogContent, query: promptToSend }),
+    signal: controller.signal
 });
+clearTimeout(timeout);
🧰 Tools
🪛 eslint

[error] 54-5454: Replace ······const·response·=·await·fetch('/api/summarize' with const·response·=·await·fetch("/api/summarize"

(prettier/prettier)


[error] 55-5555: Replace ················method:·'POST' with ········method:·"POST"

(prettier/prettier)


[error] 56-5656: Replace ················headers:·{·'Content-Type':·'application/json' with ········headers:·{·"Content-Type":·"application/json"

(prettier/prettier)


[error] 57-5757: Delete ········

(prettier/prettier)


144-159: Enhance loading state accessibility.

The loading state should be announced to screen readers. Consider adding ARIA attributes to improve accessibility.

 {isLoading && (
     <motion.div
         initial={{ opacity: 0 }}
         animate={{ opacity: 1 }}
+        role="status"
+        aria-live="polite"
         className="flex flex-col md:flex-row justify-start items-start gap-4"
     >
         <div className='min-w-9 min-h-9 md:min-w-12 md:min-h-12 border border-white flex items-center justify-center rounded-full text-white bg-black'>
             <BotIcon></BotIcon>
         </div>
         <div className="bg-[#09090b] shadow-lg shadow-black text-gray-200 p-3 rounded-[13px] flex items-center space-x-2">
             <Loader2 className="animate-spin " size={18} />
-            <span>AI is thinking...</span>
+            <span aria-label="Loading response">AI is thinking...</span>
         </div>
     </motion.div>
 )}
🧰 Tools
🪛 eslint

[error] 144-144144: Replace ···················· with ··········

(prettier/prettier)


[error] 145-145145: Replace ························ with ············

(prettier/prettier)


[error] 146-146146: Replace ···························· with ··············

(prettier/prettier)


[error] 147-147147: Replace ···························· with ··············

(prettier/prettier)


[error] 148-148148: Delete ··············

(prettier/prettier)


[error] 149-149149: Delete ············

(prettier/prettier)


[error] 150-150150: Replace ····························<div·className='min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·text-white·bg-black' with ··············<div·className="min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·text-white·bg-black"

(prettier/prettier)


[error] 151-151151: Delete ················

(prettier/prettier)


[error] 152-152152: Replace ···························· with ··············

(prettier/prettier)


[error] 153-153153: Delete ··············

(prettier/prettier)


[error] 154-155155: Delete ⏎················

(prettier/prettier)


[error] 156-156156: Replace ································ with ················

(prettier/prettier)


[error] 157-157157: Replace ···························· with ··············

(prettier/prettier)


[error] 158-158158: Delete ············

(prettier/prettier)


[error] 159-159159: Replace ···················· with ··········

(prettier/prettier)


201-213: Enhance content parsing robustness.

The content parsing could be more comprehensive and handle additional markdown patterns. Consider using a dedicated markdown parser library for better maintainability.

+import MarkdownIt from 'markdown-it';
+const md = new MarkdownIt({
+    html: false,
+    breaks: true,
+    linkify: true
+});

 const parseContent = (content: string) => {
-    const formattedContent = content.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>');
-    const paragraphWrapped = formattedContent
-        .split('\n\n')
-        .map((line) => `<p>${line.trim()}</p>`)
-        .join('');
-    return DOMPurify.sanitize(paragraphWrapped);
+    const htmlContent = md.render(content);
+    return DOMPurify.sanitize(htmlContent);
 };
🧰 Tools
🪛 eslint

[error] 202-203203: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>');⏎ with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 205-205205: Delete ··

(prettier/prettier)


[error] 206-206206: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 207-207207: Delete ····

(prettier/prettier)


[error] 208-208208: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 210-212212: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)

src/app/layout.tsx (1)

14-14: Maintain consistent quote style

Update the import statement to use double quotes to match the project's conventions.

-import { Toaster } from '@/components/ui/toaster';
+import { Toaster } from "@/components/ui/toaster"
🧰 Tools
🪛 eslint

[error] 14-1515: Replace '@/components/ui/toaster';⏎ with "@/components/ui/toaster"

(prettier/prettier)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 796158f and 867e98c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (10)
  • package.json (2 hunks)
  • src/app/blog/[slug]/page.tsx (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/components/BlogChatInterface.tsx (1 hunks)
  • src/components/BlogHeader.tsx (4 hunks)
  • src/components/BlogPostClient.tsx (1 hunks)
  • src/components/ChatSuggestions.tsx (1 hunks)
  • src/components/Navbar.tsx (1 hunks)
  • tailwind.config.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/globals.css
  • src/components/Navbar.tsx
  • package.json
🧰 Additional context used
📓 Learnings (2)
src/components/ChatSuggestions.tsx (3)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:16-27
Timestamp: 2024-12-06T20:47:07.465Z
Learning: In the `ChatSuggestions` component, keyboard accessibility for suggestions is intentionally not implemented because there are only two questions displayed, and the 'Enter' key is reserved for sending out user questions.
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:19-24
Timestamp: 2024-12-06T20:37:00.154Z
Learning: Keyboard accessibility was not added to `ChatSuggestions.tsx` because it displays only two questions.
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:19-24
Timestamp: 2024-12-06T20:37:00.154Z
Learning: In `ChatSuggestions.tsx`, clicking a suggested question replaces the component with `ChatInterface.tsx`, which handles the loading state.
src/components/BlogChatInterface.tsx (1)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/BlogChatInterface.tsx:37-42
Timestamp: 2024-12-06T20:38:27.090Z
Learning: In the `src/components/BlogChatInterface.tsx` file of our React application, the `toast` function supports the properties `color`, `duration`, and `style`, allowing customization of toast notifications.
🪛 eslint
src/components/BlogHeader.tsx

[error] 50-5050: Replace ·onClick={()·=>·setShowChat(true)}·className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·" with ⏎············onClick={()·=>·setShowChat(true)}⏎············className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"⏎··········

(prettier/prettier)

src/components/ChatSuggestions.tsx

[error] 1-11: Replace 'react' with "react"

(prettier/prettier)


[error] 2-22: Delete ;

(prettier/prettier)


[error] 5-55: Replace ····title:·string, with ··title:·string

(prettier/prettier)


[error] 6-66: Replace ····description:·string, with ··description:·string

(prettier/prettier)


[error] 7-77: Delete ··

(prettier/prettier)


[error] 7-77: 'customPrompt' is defined but never used.

(no-unused-vars)


[error] 9-1414: Replace ·title,·description,·handleSend·})·=>·{⏎⏎⏎⏎⏎ with ⏎··title,⏎··description,⏎··handleSend,⏎})·=>·{

(prettier/prettier)


[error] 15-1515: Delete ··

(prettier/prettier)


[error] 16-1616: Replace ········<div·className='w-full·flex·py-6·flex-col·gap-6·items-center' with ····<div·className="w-full·flex·py-6·flex-col·gap-6·items-center"

(prettier/prettier)


[error] 17-1717: Replace ············<h1·className='text-lg·text-center·md:text-2xl··font-[300]·text-white/70'>Go·Ahead.·Ask·<b·className='text-white·font-medium'>AI</b>·about·anything·related·to·this·<b·className='text-white·font-medium'>Blog</b>.</h1> with ······<h1·className="text-lg·text-center·md:text-2xl··font-[300]·text-white/70">⏎········Go·Ahead.·Ask·<b·className="text-white·font-medium">AI</b>·about⏎········anything·related·to·this·<b·className="text-white·font-medium">Blog</b>.

(prettier/prettier)


[error] 18-1818: Replace ······<div·className='grid·grid-cols-1·md:grid-cols-2·w-full·gap-5' with </h1>⏎······<div·className="grid·grid-cols-1·md:grid-cols-2·w-full·gap-5"

(prettier/prettier)


[error] 19-1919: Replace ················<motion.div·onClick={()·=>·{·handleSend(Make·me·understand·this·blog·:·${title}?)·}}·initial={{·y:·20,·opacity:·0·}}·whileHover={{·scale:·1.05·}}·animate={{·y:·0,·opacity:·1·}}·transition={{·duration:·0.1·}}·exit={{·y:·20,·opacity:·0·}}·className='px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer'> with ········<motion.div⏎··········onClick={()·=>·{⏎············handleSend(Make·me·understand·this·blog·:·${title}?)⏎··········}}⏎··········initial={{·y:·20,·opacity:·0·}}⏎··········whileHover={{·scale:·1.05·}}⏎··········animate={{·y:·0,·opacity:·1·}}⏎··········transition={{·duration:·0.1·}}⏎··········exit={{·y:·20,·opacity:·0·}}⏎··········className="px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer"

(prettier/prettier)


[error] 20-2020: Replace ·· with >⏎

(prettier/prettier)


[error] 21-2121: Delete ········

(prettier/prettier)


[error] 22-2222: Replace ················<motion.div·onClick={()·=>·{·handleSend(What·does·"${description}"·means·in·this·blog?·)·}}·initial={{·y:·20,·opacity:·0·}}·whileHover={{·scale:·1.05·}}·animate={{·y:·0,·opacity:·1·}}·transition={{·duration:·0.1·}}·exit={{·y:·20,·opacity:·0·}}·className='px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer'> with ········<motion.div⏎··········onClick={()·=>·{⏎············handleSend(What·does·"${description}"·means·in·this·blog?·)⏎··········}}⏎··········initial={{·y:·20,·opacity:·0·}}⏎··········whileHover={{·scale:·1.05·}}⏎··········animate={{·y:·0,·opacity:·1·}}⏎··········transition={{·duration:·0.1·}}⏎··········exit={{·y:·20,·opacity:·0·}}⏎··········className="px-4·py-5·border·border-white/40·rounded-[14px]·bg-black/70·text-sm·font-[200]·text-white/60·w-full·hover:bg-black·hover:text-white/80·cursor-pointer"

(prettier/prettier)


[error] 23-2323: Replace ·· with >⏎

(prettier/prettier)


[error] 24-2424: Replace ················ with ········

(prettier/prettier)


[error] 25-2525: Replace ············ with ······

(prettier/prettier)


[error] 26-2626: Replace ········ with ····

(prettier/prettier)


[error] 27-2727: Delete ··

(prettier/prettier)

src/components/BlogPostClient.tsx

[error] 3-33: Replace 'react'; with "react"

(prettier/prettier)


[error] 4-44: Replace '@/components/BlogChatInterface'; with "@/components/BlogChatInterface"

(prettier/prettier)


[error] 5-55: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 6-66: Replace '@/components/ViewCounter'; with "@/components/ViewCounter"

(prettier/prettier)


[error] 7-77: Replace '@/components/SocialMetadata'; with "@/components/SocialMetadata"

(prettier/prettier)


[error] 8-88: Replace '@/components/SocialShare'; with "@/components/SocialShare"

(prettier/prettier)


[error] 11-1111: Delete ··

(prettier/prettier)


[error] 12-1212: Replace ···· with ··

(prettier/prettier)


[error] 13-1313: Delete ··

(prettier/prettier)


[error] 14-1414: Delete ··

(prettier/prettier)


[error] 15-1515: Replace ···· with ··

(prettier/prettier)


[error] 16-1616: Delete ··

(prettier/prettier)


[error] 17-1717: Delete ··

(prettier/prettier)


[error] 21-2121: Delete ··

(prettier/prettier)


[error] 22-2222: Delete ··

(prettier/prettier)


[error] 23-2323: Delete ··

(prettier/prettier)


[error] 24-2424: Replace ···· with ··

(prettier/prettier)


[error] 25-2525: Delete ··

(prettier/prettier)


[error] 26-2626: Replace ···· with ··

(prettier/prettier)


[error] 27-2727: Delete ··

(prettier/prettier)


[error] 29-3333: Replace ··const·[showChat,·setShowChat]·=·useState(false)⏎⏎⏎⏎ with const·[showChat,·setShowChat]·=·useState(false)

(prettier/prettier)


[error] 35-3535: Delete ··

(prettier/prettier)


[error] 36-3636: Replace ····const·date·=·new·Date(dateString); with const·date·=·new·Date(dateString)

(prettier/prettier)


[error] 37-3737: Replace ····return·date.toLocaleDateString('en-US',·{·year:·'numeric',·month:·'long',·day:·'numeric'·}); with return·date.toLocaleDateString("en-US",·{⏎······year:·"numeric",⏎······month:·"long",⏎······day:·"numeric",

(prettier/prettier)


[error] 38-3939: Replace ;⏎ with )

(prettier/prettier)


[error] 40-4040: Replace ··const·baseUrl·=·'https://devtoolsacademy.com'; with }⏎⏎··const·baseUrl·=·"https://devtoolsacademy.com"

(prettier/prettier)


[error] 41-4141: Replace ····const·postUrl·=·${baseUrl}/blog/${slug}; with ··const·postUrl·=·${baseUrl}/blog/${slug}``

(prettier/prettier)


[error] 43-4343: Delete ··

(prettier/prettier)


[error] 44-4444: Delete ····

(prettier/prettier)


[error] 45-4545: Replace ············ with ······

(prettier/prettier)


[error] 46-4646: Replace ················ with ········

(prettier/prettier)


[error] 47-4747: Replace ················ with ········

(prettier/prettier)


[error] 48-4848: Delete ········

(prettier/prettier)


[error] 49-4949: Replace ················ with ········

(prettier/prettier)


[error] 50-5050: Replace ················ with ········

(prettier/prettier)


[error] 51-5151: Delete ······

(prettier/prettier)


[error] 52-5252: Replace ············ with ······

(prettier/prettier)


[error] 53-5353: Delete ······

(prettier/prettier)


[error] 54-5454: Replace ················ with ········

(prettier/prettier)


[error] 55-5555: Replace ····················<span·className="text-gray-400">{formatDate(publishedAt)}</span> with ··········<span·className="text-gray-400">{formatDate(publishedAt)}</span>{"·"}⏎·········

(prettier/prettier)


[error] 56-5656: Replace ··········<button·onClick={()·=>·setShowChat(true)}·className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"> with <button⏎············onClick={()·=>·setShowChat(true)}⏎············className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"

(prettier/prettier)


[error] 57-5757: Replace ·· with >⏎

(prettier/prettier)


[error] 58-5858: Replace ···················· with ··········

(prettier/prettier)


[error] 59-5959: Delete ········

(prettier/prettier)


[error] 60-6060: Delete ········

(prettier/prettier)


[error] 61-6161: Delete ··········

(prettier/prettier)


[error] 62-6262: Replace ···················· with ··········

(prettier/prettier)


[error] 63-6363: Delete ········

(prettier/prettier)


[error] 64-6464: Delete ······

(prettier/prettier)


[error] 65-6565: Delete ······

(prettier/prettier)


[error] 66-6666: Replace ············ with ······

(prettier/prettier)


[error] 67-6767: Delete ········

(prettier/prettier)


[error] 68-6868: Delete ··········

(prettier/prettier)


[error] 69-6969: Replace ···················· with ··········

(prettier/prettier)


[error] 70-7070: Replace ···················· with ··········

(prettier/prettier)


[error] 71-7171: Replace ···················· with ··········

(prettier/prettier)


[error] 72-7272: Delete ········

(prettier/prettier)


[error] 73-7373: Replace ············ with ······

(prettier/prettier)


[error] 74-7474: Replace ········ with ····

(prettier/prettier)


[error] 75-7575: Replace ····); with ··)

(prettier/prettier)


[error] 76-7676: Delete ;

(prettier/prettier)

tailwind.config.ts

[error] 105-105105: 'theme' is defined but never used.

(no-unused-vars)

src/components/BlogChatInterface.tsx

[error] 1-11: Replace 'react'; with "react"

(prettier/prettier)


[error] 2-22: Replace 'framer-motion'; with "framer-motion"

(prettier/prettier)


[error] 3-33: Replace 'lucide-react'; with "lucide-react"

(prettier/prettier)


[error] 4-44: Replace '@heroicons/react/24/outline'; with "@heroicons/react/24/outline"

(prettier/prettier)


[error] 5-55: Replace './ChatSuggestions'; with "./ChatSuggestions"

(prettier/prettier)


[error] 6-66: Replace 'dompurify'; with "dompurify"

(prettier/prettier)


[error] 7-77: Delete ;

(prettier/prettier)


[error] 8-88: Replace '@/hooks/use-toast'; with "@/hooks/use-toast"

(prettier/prettier)


[error] 11-1111: Delete ··

(prettier/prettier)


[error] 12-1212: Replace ···· with ··

(prettier/prettier)


[error] 16-1616: Replace ··blogContent:·string; with blogContent:·string

(prettier/prettier)


[error] 17-1717: Replace ····blogTitle:·string; with ··blogTitle:·string

(prettier/prettier)


[error] 18-1818: Replace ····blogDescription:·string; with ··blogDescription:·string

(prettier/prettier)


[error] 19-1919: Replace ··onClose:·()·=>·void; with onClose:·()·=>·void

(prettier/prettier)


[error] 22-2222: Replace ·blogContent,·blogTitle,·blogDescription,·onClose· with ⏎··blogContent,⏎··blogTitle,⏎··blogDescription,⏎··onClose,⏎

(prettier/prettier)


[error] 23-2323: Replace ····const·[messages,·setMessages]·=·useState<ChatMessage[]>([]); with ··const·[messages,·setMessages]·=·useState<ChatMessage[]>([])

(prettier/prettier)


[error] 24-2424: Replace ····const·[input,·setInput]·=·useState(''); with ··const·[input,·setInput]·=·useState("")

(prettier/prettier)


[error] 25-2525: Replace ····const·[isLoading,·setIsLoading]·=·useState(false); with ··const·[isLoading,·setIsLoading]·=·useState(false)

(prettier/prettier)


[error] 26-2626: Replace ····const·messagesEndRef·=·useRef<HTMLDivElement>(null); with ··const·messagesEndRef·=·useRef<HTMLDivElement>(null)

(prettier/prettier)


[error] 27-2727: Replace ····const·{·toast·}·=·useToast(); with ··const·{·toast·}·=·useToast()

(prettier/prettier)


[error] 28-2828: Replace ···· with ··

(prettier/prettier)


[error] 29-2929: Replace ········messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·}); with ····messagesEndRef.current?.scrollIntoView({·behavior:·"smooth"·})

(prettier/prettier)


[error] 30-3030: Replace ····}; with ··}

(prettier/prettier)


[error] 32-3232: Replace ···· with ··

(prettier/prettier)


[error] 34-3434: Delete ··

(prettier/prettier)


[error] 35-3535: Replace ········const·promptToSend·=·customPrompt·||·input; with ····const·promptToSend·=·customPrompt·||·input

(prettier/prettier)


[error] 36-3636: Replace ········ with ····

(prettier/prettier)


[error] 37-3737: Replace ············ with ······

(prettier/prettier)


[error] 38-3838: Replace ················variant:·'destructive' with ········variant:·"destructive"

(prettier/prettier)


[error] 39-3939: Replace ················color:·'gray' with ········color:·"gray"

(prettier/prettier)


[error] 40-4040: Replace ················ with ········

(prettier/prettier)


[error] 41-4141: Replace ········style:·{·background:·'#7f1d1d',·color:·'white',·border:·'gray',·borderRadius:·'5px'·} with style:·{⏎··········background:·"#7f1d1d",⏎··········color:·"white",⏎··········border:·"gray",⏎··········borderRadius:·"5px"

(prettier/prettier)


[error] 42-4242: Replace ········description:·"Please·enter·valid·input." with },⏎········description:·"Please·enter·valid·input.",

(prettier/prettier)


[error] 43-4343: Replace ············ with ······

(prettier/prettier)


[error] 45-4545: Replace ············return; with ······return

(prettier/prettier)


[error] 46-4646: Replace ····}; with }

(prettier/prettier)


[error] 48-4848: Replace ········const·userMessage:·ChatMessage·=·{·role:·'human',·content:·promptToSend·}; with ····const·userMessage:·ChatMessage·=·{·role:·"human",·content:·promptToSend·}

(prettier/prettier)


[error] 49-4949: Replace ····setMessages(prev·=>·[...prev,·userMessage]); with setMessages((prev)·=>·[...prev,·userMessage])

(prettier/prettier)


[error] 50-5050: Replace ········setInput(''); with ····setInput("")

(prettier/prettier)


[error] 51-5151: Replace ········setIsLoading(true); with ····setIsLoading(true)

(prettier/prettier)


[error] 53-5353: Delete ····

(prettier/prettier)


[error] 54-5454: Replace ······const·response·=·await·fetch('/api/summarize' with const·response·=·await·fetch("/api/summarize"

(prettier/prettier)


[error] 55-5555: Replace ················method:·'POST' with ········method:·"POST"

(prettier/prettier)


[error] 56-5656: Replace ················headers:·{·'Content-Type':·'application/json' with ········headers:·{·"Content-Type":·"application/json"

(prettier/prettier)


[error] 57-5757: Delete ········

(prettier/prettier)


[error] 58-5959: Replace ············});⏎ with ······})

(prettier/prettier)


[error] 61-6262: Delete ⏎······

(prettier/prettier)


[error] 63-6363: Replace ················const·data·=·await·response.json(); with ········const·data·=·await·response.json()

(prettier/prettier)


[error] 64-6464: Replace ················setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·"Error:·"·+·data.error·}]); with ········setMessages((prev)·=>·[⏎··········...prev,⏎··········{·role:·"assistant",·content:·"Error:·"·+·data.error·},

(prettier/prettier)


[error] 65-6565: Replace ········return; with ])⏎········return

(prettier/prettier)


[error] 66-6767: Replace ⏎············ with ······

(prettier/prettier)


[error] 68-6868: Replace ············const·data·=·await·response.json(); with ······const·data·=·await·response.json()

(prettier/prettier)


[error] 70-7070: Replace ············const·responseAnswer·=·parseContent(data.answer); with ······const·responseAnswer·=·parseContent(data.answer)

(prettier/prettier)


[error] 72-7272: Replace ············setMessages(prev·=>·[...prev,·{·role:·'assistant',·content:·responseAnswer·}]); with ······setMessages((prev)·=>·[⏎········...prev,⏎········{·role:·"assistant",·content:·responseAnswer·},

(prettier/prettier)


[error] 73-7373: Insert ··])⏎

(prettier/prettier)


[error] 74-7474: Replace ············console.error('Error·in·chat:',·error); with ······console.error("Error·in·chat:",·error)

(prettier/prettier)


[error] 75-7575: Replace ······const·errorMessage·= with const·errorMessage·=⏎·······

(prettier/prettier)


[error] 76-7676: Replace ················ with ··········

(prettier/prettier)


[error] 77-7777: Replace ················:·"An·unexpected·error·occurred.·Please·try·again."; with ··········:·"An·unexpected·error·occurred.·Please·try·again."

(prettier/prettier)


[error] 78-7878: Replace ············setMessages(prev·=>·[...prev,·{ with ······setMessages((prev)·=>·[⏎········...prev,

(prettier/prettier)


[error] 79-7979: Replace ·········role:·'assistant' with ·{⏎··········role:·"assistant"

(prettier/prettier)


[error] 80-8080: Replace ················content:·errorMessage with ··········content:·errorMessage,

(prettier/prettier)


[error] 81-8181: Replace ············}]) with ········},

(prettier/prettier)


[error] 82-8282: Replace ··} with ])

(prettier/prettier)


[error] 83-8383: Replace ··· with }

(prettier/prettier)


[error] 84-8484: Replace ······setIsLoading(false); with setIsLoading(false)

(prettier/prettier)


[error] 85-8585: Replace ········ with ····

(prettier/prettier)


[error] 86-8686: Replace ····}; with ··}

(prettier/prettier)


[error] 87-8787: Delete ··

(prettier/prettier)


[error] 88-8888: Replace ········ with ····

(prettier/prettier)


[error] 89-8989: Delete ··

(prettier/prettier)


[error] 90-9090: Delete ··

(prettier/prettier)


[error] 91-9191: Replace ········ with ····

(prettier/prettier)


[error] 92-9292: Replace ············ with ······

(prettier/prettier)


[error] 93-9393: Replace ············ with ······

(prettier/prettier)


[error] 94-9494: Delete ······

(prettier/prettier)


[error] 95-9595: Delete ······

(prettier/prettier)


[error] 96-9696: Delete ····

(prettier/prettier)


[error] 97-9797: Replace ············ with ······

(prettier/prettier)


[error] 98-9898: Delete ········

(prettier/prettier)


[error] 99-9999: Delete ········

(prettier/prettier)


[error] 100-100100: Replace ················ with ········

(prettier/prettier)


[error] 101-101101: Delete ······

(prettier/prettier)


[error] 102-102102: Replace ················ with ········

(prettier/prettier)


[error] 103-103103: Replace ····················<h2·className="text-xl·md:text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">Chat·about·the·Blog·<ChatBubbleLeftIcon·className='w-7·h-7'></ChatBubbleLeftIcon></h2> with ··········<h2·className="text-xl·md:text-3xl·font-sans·font-medium··flex-1·flex·justify-center·items-center·gap-4·text-center··text-[#eaeaea]">⏎············Chat·about·the·Blog{"·"}⏎············<ChatBubbleLeftIcon·className="w-7·h-7"></ChatBubbleLeftIcon>

(prettier/prettier)


[error] 104-104104: Insert </h2>⏎

(prettier/prettier)


[error] 105-105105: Delete ············

(prettier/prettier)


[error] 106-106106: Replace ···················· with ··········

(prettier/prettier)


[error] 107-107107: Replace ················ with ········

(prettier/prettier)


[error] 108-108108: Delete ········

(prettier/prettier)


[error] 109-109109: Delete ··········

(prettier/prettier)


[error] 110-110110: Replace ························{messages.length·==·0·?·<ChatSuggestions·title={blogTitle}·description={blogDescription}·handleSend={handleSend}></ChatSuggestions·>·:· with ············{messages.length·==·0·?·(⏎··············<ChatSuggestions⏎················title={blogTitle}⏎················description={blogDescription}⏎················handleSend={handleSend}⏎··············></ChatSuggestions>⏎············)·:·(⏎··············

(prettier/prettier)


[error] 111-111111: Replace ····························<motion.div·key={index} with ················<motion.div

(prettier/prettier)


[error] 112-112112: Insert ····key={index}⏎

(prettier/prettier)


[error] 113-113113: Replace ································ with ··················

(prettier/prettier)


[error] 114-114114: Replace ································exit={{·opacity:·0,·y:·-20·}}·className='flex·flex-col··gap-4·'> with ··················exit={{·opacity:·0,·y:·-20·}}⏎··················className="flex·flex-col··gap-4·"

(prettier/prettier)


[error] 115-115115: Replace ················<div·className='flex·flex-col·md:flex-row·gap-4·items-start' with >⏎··················<div·className="flex·flex-col·md:flex-row·gap-4·items-start"

(prettier/prettier)


[error] 116-116116: Replace ····································<div·className='min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·text-white·bg-black·rounded-full·' with ····················<div·className="min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·shadow-lg·border-white/40·shadow-black·flex·items-center·justify-center·text-white·bg-black·rounded-full·"

(prettier/prettier)


[error] 117-117117: Replace ········································{msg.role·===·"human"·?·<User·className='text-white'></User>·:·<BotIcon></BotIcon>} with ······················{msg.role·===·"human"·?·(⏎························<User·className="text-white"></User>⏎······················)·:·(

(prettier/prettier)


[error] 118-118118: Replace ············</div with <BotIcon></BotIcon

(prettier/prettier)


[error] 119-119119: Replace ···············<motion.div with ·)}⏎····················</div>

(prettier/prettier)


[error] 120-120120: Insert ··<motion.div⏎

(prettier/prettier)


[error] 121-121121: Replace ········································ with ······················

(prettier/prettier)


[error] 122-122122: Replace ········································ with ······················

(prettier/prettier)


[error] 123-123123: Replace ········································className={flex·${msg.role·===·'human'·?·'justify-end'·:·'justify-start'with······················className={flex·${msg.role·===·"human"·?·"justify-end"·:·"justify-start"

(prettier/prettier)


[error] 124-124124: Replace ···································· with ····················

(prettier/prettier)


[error] 125-125125: Replace ·······················<div·className={max-w-full·text-[16px]·md:leading-relaxed·md:tracking-wide·shadow-lg·shadow-black·px-3·md:px-5·py-4·rounded-xl·${msg.role·===·'human'·?·'bg-white/80·text-black/80'·:·'bg-[#09090b]·text-gray-200'with·····<div⏎························className={max-w-full·text-[16px]·md:leading-relaxed·md:tracking-wide·shadow-lg·shadow-black·px-3·md:px-5·py-4·rounded-xl·${⏎··························msg.role·===·"human"⏎····························?·"bg-white/80·text-black/80"⏎····························:·"bg-[#09090b]·text-gray-200"

(prettier/prettier)


[error] 126-126126: Replace ············································ with ························

(prettier/prettier)


[error] 127-127127: Replace ········································ with ······················

(prettier/prettier)


[error] 128-128128: Replace ············································{msg.role·===·'human' with ························{msg.role·===·"human"

(prettier/prettier)


[error] 129-131131: Replace ················································<>⏎····················································{msg.content}⏎················································ with ··························<>{msg.content}

(prettier/prettier)


[error] 132-132132: Replace ············································ with ························

(prettier/prettier)


[error] 133-135135: Replace ················································<>⏎····················································{parse(msg.content)}⏎················································ with ··························<>{parse(msg.content)}

(prettier/prettier)


[error] 136-136136: Replace ············································ with ························

(prettier/prettier)


[error] 137-137137: Replace ········································ with ······················

(prettier/prettier)


[error] 138-138138: Replace ···································· with ····················

(prettier/prettier)


[error] 139-139139: Replace ································ with ··················

(prettier/prettier)


[error] 140-140140: Replace ··············<div·className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}></div> with <div⏎····················className={w-full·h-[0.5px]·${index·===·messages.length·-·1·?·"hidden"·:·"block"}·bg-gray-500/50·my-3}

(prettier/prettier)


[error] 141-141141: Replace ············ with ··················></div>⏎

(prettier/prettier)


[error] 142-142142: Replace ··········)) with ))⏎············)

(prettier/prettier)


[error] 143-143143: Delete ··········

(prettier/prettier)


[error] 144-144144: Replace ···················· with ··········

(prettier/prettier)


[error] 145-145145: Replace ························ with ············

(prettier/prettier)


[error] 146-146146: Replace ···························· with ··············

(prettier/prettier)


[error] 147-147147: Replace ···························· with ··············

(prettier/prettier)


[error] 148-148148: Delete ··············

(prettier/prettier)


[error] 149-149149: Delete ············

(prettier/prettier)


[error] 150-150150: Replace ····························<div·className='min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·text-white·bg-black' with ··············<div·className="min-w-9·min-h-9·md:min-w-12·md:min-h-12·border·border-white·flex·items-center·justify-center·rounded-full·text-white·bg-black"

(prettier/prettier)


[error] 151-151151: Delete ················

(prettier/prettier)


[error] 152-152152: Replace ···························· with ··············

(prettier/prettier)


[error] 153-153153: Delete ··············

(prettier/prettier)


[error] 154-155155: Delete ⏎················

(prettier/prettier)


[error] 156-156156: Replace ································ with ················

(prettier/prettier)


[error] 157-157157: Replace ···························· with ··············

(prettier/prettier)


[error] 158-158158: Delete ············

(prettier/prettier)


[error] 159-159159: Replace ···················· with ··········

(prettier/prettier)


[error] 160-160160: Delete ··········

(prettier/prettier)


[error] 161-161161: Replace ················ with ········

(prettier/prettier)


[error] 162-162162: Delete ········

(prettier/prettier)


[error] 163-163163: Replace ···················· with ··········

(prettier/prettier)


[error] 164-164164: Delete ············

(prettier/prettier)


[error] 164-174174: The element input has an implicit role of textbox. Defining this explicitly is redundant and should be avoided.

(jsx-a11y/no-redundant-roles)


[error] 165-165165: Replace ···························· with ··············

(prettier/prettier)


[error] 166-166166: Replace ···························· with ··············

(prettier/prettier)


[error] 167-167167: Replace ···························· with ··············

(prettier/prettier)


[error] 168-168168: Delete ··············

(prettier/prettier)


[error] 169-169169: Replace ···························· with ··············

(prettier/prettier)


[error] 170-170170: Replace ···························· with ··············

(prettier/prettier)


[error] 171-171171: Replace ····························onKeyPress={(e)·=>·e.key·===·'Enter' with ··············onKeyPress={(e)·=>·e.key·===·"Enter"

(prettier/prettier)


[error] 172-172172: Delete ··············

(prettier/prettier)


[error] 173-173173: Delete ··············

(prettier/prettier)


[error] 174-174174: Delete ············

(prettier/prettier)


[error] 175-175175: Replace ············<button·onClick={handleSummary}·className='bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black'>Summarize with <button⏎··············onClick={handleSummary}⏎··············className="bg-white·hidden·md:block·text-xs·text-black·hover:bg-white/60·rounded·px-4·border·border-black"⏎············>⏎··············Summarize{"·"}⏎···········

(prettier/prettier)


[error] 177-177177: Replace ············<button with <button⏎·············

(prettier/prettier)


[error] 178-178178: Replace ··············disabled={isLoading}·className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"> with disabled={isLoading}⏎··············className="bg-slate-800·no-underline·group·cursor-pointer·relative·shadow-2xl·shadow-zinc-900·rounded-full·p-px·text-xs·font-semibold·leading-6··text-white·inline-block"

(prettier/prettier)


[error] 179-179179: Replace ·· with >⏎

(prettier/prettier)


[error] 180-180180: Replace ································ with ················

(prettier/prettier)


[error] 181-181181: Replace ···························· with ··············

(prettier/prettier)


[error] 182-182182: Delete ··············

(prettier/prettier)


[error] 183-183183: Delete ················

(prettier/prettier)


[error] 184-184184: Delete ··················

(prettier/prettier)


[error] 185-186186: Replace ⏎································ with ················

(prettier/prettier)


[error] 187-188188: Replace ⏎···························· with ··············

(prettier/prettier)


[error] 189-189189: Delete ··············

(prettier/prettier)


[error] 190-191191: Replace ············</button>⏎ with </button>

(prettier/prettier)


[error] 192-192192: Delete ··········

(prettier/prettier)


[error] 193-193193: Delete ········

(prettier/prettier)


[error] 194-194194: Replace ············ with ······

(prettier/prettier)


[error] 195-195195: Replace ········ with ····

(prettier/prettier)


[error] 196-196196: Replace ····); with ··)

(prettier/prettier)


[error] 197-199199: Delete ;⏎⏎

(prettier/prettier)


[error] 202-203203: Replace ····const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·'<b>$1</b>');⏎ with ··const·formattedContent·=·content.replace(/\*\*(.*?)\*\*/g,·"<b>$1</b>")

(prettier/prettier)


[error] 205-205205: Delete ··

(prettier/prettier)


[error] 206-206206: Replace ····.split('\n\n' with .split("\n\n"

(prettier/prettier)


[error] 207-207207: Delete ····

(prettier/prettier)


[error] 208-208208: Replace ········.join(''); with ····.join("")

(prettier/prettier)


[error] 210-212212: Replace ····return·DOMPurify.sanitize(paragraphWrapped);⏎⏎ with ··return·DOMPurify.sanitize(paragraphWrapped)

(prettier/prettier)


[error] 213-217217: Delete ;⏎⏎⏎⏎

(prettier/prettier)


[error] 219-219219: Replace ; with

(prettier/prettier)

src/app/layout.tsx

[error] 14-1515: Replace '@/components/ui/toaster';⏎ with "@/components/ui/toaster"

(prettier/prettier)

🔇 Additional comments (13)
tailwind.config.ts (3)

Line range hint 18-47: Well-structured color system and border radius configuration!

The use of HSL color variables and calculated border radius values provides excellent flexibility for theming and consistent styling across the application.

🧰 Tools
🪛 eslint

[error] 105-105105: 'theme' is defined but never used.

(no-unused-vars)


48-51: Clean font family configuration!

The use of CSS variables for font families allows for easy customization and consistent typography across the application.


98-104: Well-defined animation configurations!

The animation timings and easing functions are appropriately configured for smooth transitions.

src/components/BlogHeader.tsx (4)

13-13: Addition of the description prop.

It's good practice to include the blog description in the header to provide better context for the user and other components. Ensure that other files calling this component supply the description prop to avoid runtime errors.


22-22: Destructuring description.

Destructuring the new property here is consistent with the rest of the code. Ensure that the prop remains optional vs required only if your business logic demands it. If description can be undefined, add default checks.


50-51: Enhanced button label and styling.

Renaming the button to "Summarize with AI" is more descriptive. The shimmering animation is a creative UX detail. However, confirm if the visual effect aligns with accessibility guidelines, specifically ensuring an accessible color contrast ratio for text.

🧰 Tools
🪛 eslint

[error] 50-5050: Replace ·onClick={()·=>·setShowChat(true)}·className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·" with ⏎············onClick={()·=>·setShowChat(true)}⏎············className="inline-flex·h-10·animate-shimmer·items-center·justify-center··border·border-slate-800·bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)]·bg-[length:200%_100%]·px-6·font-medium·text-slate-200·transition-colors·focus:outline-none·focus:ring-2·focus:ring-slate-400·focus:ring-offset-2·focus:ring-offset-slate-50·rounded-full·text-sm·"⏎··········

(prettier/prettier)


61-62: Passing description to BlogChatInterface.

Propagating the description to the BlogChatInterface is beneficial for summarization or contextual queries. Double-check if your chat interface effectively handles empty or overly long descriptions.

src/app/blog/[slug]/page.tsx (1)

135-135: New prop description passed to BlogHeader.

This integration helps the header display a blog's brief summary. Ensure consistency throughout the codebase where the component is consumed, since description is now required in BlogHeader.

src/components/ChatSuggestions.tsx (1)

4-8: Interface definition for BlogChatSuggestionProps.

Type definition is clearly stated, making the code self-documenting. Confirm whether any default or optional properties are needed if your design can accommodate missing values.

🧰 Tools
🪛 eslint

[error] 5-55: Replace ····title:·string, with ··title:·string

(prettier/prettier)


[error] 6-66: Replace ····description:·string, with ··description:·string

(prettier/prettier)


[error] 7-77: Delete ··

(prettier/prettier)


[error] 7-77: 'customPrompt' is defined but never used.

(no-unused-vars)

src/components/BlogPostClient.tsx (2)

10-18: Definition of BlogPostClientProps.

The new interface provides clear typing for the blog post data. Confirm if any of these fields might be optional to handle incomplete or placeholder data gracefully.
[approve]

🧰 Tools
🪛 eslint

[error] 11-1111: Delete ··

(prettier/prettier)


[error] 12-1212: Replace ···· with ··

(prettier/prettier)


[error] 13-1313: Delete ··

(prettier/prettier)


[error] 14-1414: Delete ··

(prettier/prettier)


[error] 15-1515: Replace ···· with ··

(prettier/prettier)


[error] 16-1616: Delete ··

(prettier/prettier)


[error] 17-1717: Delete ··

(prettier/prettier)


68-70: Ensuring blogContent, blogDescription, and blogTitle are validated.

These props enhance the context for the chat interface. If these values can be large or empty, ensure the interface handles extremes gracefully (e.g., length checks, fallback text).

🧰 Tools
🪛 eslint

[error] 68-6868: Delete ··········

(prettier/prettier)


[error] 69-6969: Replace ···················· with ··········

(prettier/prettier)


[error] 70-7070: Replace ···················· with ··········

(prettier/prettier)

src/app/layout.tsx (2)

105-110: Well-structured layout implementation!

The flex container with min-h-screen ensures full viewport height, while flex-grow on the main content area properly distributes space. The container classes provide consistent horizontal spacing across different breakpoints.


111-111: Verify toast configuration for error handling

The Toaster component is correctly positioned for application-wide notifications. Since this is part of the chatbot error handling feature, ensure that the toast configuration in @/components/ui/toaster is properly set up for error states.

✅ Verification successful

Let me try different commands to verify the toast configuration.


Let me try one more time with simpler commands.


Let me check the actual toast configuration and error handling implementation.


Toast configuration is properly set up for error handling

The toast configuration is correctly implemented with appropriate error handling:

  • The toast component is properly configured with variants including 'destructive' for error states
  • Error handling in BlogChatInterface uses the toast with correct variant and styling for error messages
  • The toast implementation includes proper timeout handling and limit controls (TOAST_LIMIT = 1)
  • Error states are properly handled with descriptive messages in the catch blocks

Example of proper error toast usage found in BlogChatInterface:

toast({
    variant: 'destructive',
    color: 'gray',
    duration: 1000,
    style: { background: '#7f1d1d', color: 'white', border: 'gray', borderRadius: '5px' },
    description: "Please enter valid input."
})
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check toast configuration and usage
echo "Checking toast configuration..."
rg -l "useToast|toast\." --type tsx

echo "\nChecking error handling patterns..."
ast-grep --pattern 'catch ($_) {
  $$$
  toast({
    $$$
  })
  $$$
}'

Length of output: 296


Script:

#!/bin/bash
# Check toast configuration and usage with correct file extension
echo "Checking toast configuration..."
rg -l "useToast|toast\." --type ts --type jsx --type tsx

echo "\nChecking for Toaster component definition..."
fd -e ts -e tsx -e jsx Toaster

echo "\nChecking error handling patterns..."
rg "toast\(.*\{.*variant.*:" --type ts --type tsx --type jsx -A 2

Length of output: 476


Script:

#!/bin/bash
# Search for toast-related files and configurations
echo "Searching for toast-related files..."
find . -type f -exec grep -l "toast" {} \;

echo "\nChecking for Toaster component..."
find . -type f -name "*oast*.tsx" -o -name "*oast*.ts" -o -name "*oast*.jsx"

echo "\nLooking for error handling with toast..."
grep -r "toast" . --include="*.ts" --include="*.tsx" --include="*.jsx" -A 2

Length of output: 2394


Script:

#!/bin/bash
# Check toast configuration files
echo "Content of toast configuration files:"
cat src/components/ui/toast.tsx
cat src/components/ui/toaster.tsx
cat src/hooks/use-toast.ts

echo "\nChecking error handling in BlogChatInterface:"
cat src/components/BlogChatInterface.tsx

Length of output: 20728

@nirnejak
Copy link
Collaborator

nirnejak commented Jan 7, 2025

@Rahulsoni9321 there seems to be some conflicts, can you please check?

@Rahulsoni9321
Copy link
Author

@Rahulsoni9321 there seems to be some conflicts, can you please check?

Hey @nirnejak, I have resolved all the conflicts, Please have a look.

@tyaga001
Copy link
Owner

@nirnejak Is it ready to merge?

@nirnejak
Copy link
Collaborator

@Rahulsoni9321 can you please pull latest from PR #69

@nirnejak
Copy link
Collaborator

@Rahulsoni9321 , can you please pull the latest changes and resolve the conflicts?

@Rahulsoni9321
Copy link
Author

@Rahulsoni9321 , can you please pull the latest changes and resolve the conflicts?

Sure @nirnejak, will do that.

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: 1

🧹 Nitpick comments (11)
src/app/blog/enhancing-web-experiences-with-the-view-transitions-api/page.mdx (1)

20-22: Rich Descriptive Metadata:
The new description prop clearly explains how the View Transitions API improves user engagement through smooth animations and transitions. This added context will aid both SEO and reader comprehension.

Consider reviewing the punctuation in the description to ensure consistency (for example, verify that end-of-sentence markers are uniformly applied across the project).

🧰 Tools
🪛 LanguageTool

[uncategorized] ~22-~22: Loose punctuation mark.
Context: ...ing user engagement and performance." } author={"Jitendra Nirnejak"} publis...

(UNLIKELY_OPENING_PUNCTUATION)

src/components/BlogPostClient.tsx (5)

3-8: Consider uncommenting or removing unused imports.

Several imports are commented out. If these components will be used in the future, consider adding TODO comments explaining when they'll be uncommented. If they're no longer needed, consider removing them entirely to keep the codebase clean.


31-34: Remove unnecessary empty lines.

These consecutive empty lines don't serve any purpose and should be removed to maintain code cleanliness.

    const [showChat, setShowChat] = useState(false)

-
-
-
-
    const formatDate = (dateString: string) => {

35-38: Consider memoizing the formatDate function.

This function is recreated on each render but doesn't depend on component state. Consider using useCallback to memoize it for better performance.

-    const formatDate = (dateString: string) => {
-        const date = new Date(dateString);
-        return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
-    };
+    const formatDate = useCallback((dateString: string) => {
+        const date = new Date(dateString);
+        return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
+    }, []);

Don't forget to import useCallback:

-import React, { useState } from 'react';
+import React, { useState, useCallback } from 'react';

40-41: Consider making baseUrl configurable.

Hardcoding the baseUrl might cause issues in different environments. Consider using an environment variable instead.

-    const baseUrl = 'https://devtoolsacademy.com';
+    const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://devtoolsacademy.com';

56-58: Add aria-label for better accessibility.

The "Summarize with AI" button should have an aria-label for better accessibility.

-                    <button onClick={() => setShowChat(true)} className="inline-flex h-10 animate-shimmer items-center justify-center  border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-6 font-medium text-slate-200 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50 rounded-full text-sm ">
+                    <button onClick={() => setShowChat(true)} aria-label="Summarize blog with AI" className="inline-flex h-10 animate-shimmer items-center justify-center  border border-slate-800 bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] px-6 font-medium text-slate-200 transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50 rounded-full text-sm ">
                         Summarize with AI
                     </button>
package.json (1)

38-49: Consider adding documentation for new UI dependencies.

Adding these UI and formatting dependencies is a good enhancement. Consider documenting how these should be used in the project to maintain consistency.

Do you have developer documentation on how these tools should be used? If not, consider creating a simple guide for your team.

src/components/blog/BlogChatInterface.tsx (4)

92-95: Clean up commented animation code.

There are several commented-out animation properties throughout the file. Either remove them entirely or add TODO comments explaining why they're preserved.

    <div
-      // initial={{ opacity: 0 }}
-      // animate={{ opacity: 1 }}
-      // exit={{ opacity: 0 }}

      className="fixed inset-0 bg-black z-50 backdrop-blur-sm bg-opacity-20 overflow-y-auto h-full w-full flex items-center justify-center p-4"
    >

Also applies to: 116-119, 125-128


99-102: Fix formatting inconsistency.

The closing bracket > is misaligned due to commented code. Either remove the commented code or align the bracket properly.

      <div
        className="bg-[#18181a] rounded-xl  border-[1px] border-white/30 w-full max-w-4xl max-h-[calc(100vh-2rem)] mx-auto shadow-md shadow-neutral-400/30 overflow-hidden"
-      // initial={{ y: 20, opacity: 0 }}
-      // animate={{ y: 0, opacity: 1 }}
+      >
-      >

180-180: Make Summarize button more accessible.

The Summarize button should have an aria-label for better accessibility.

-            <button onClick={handleSummary} className='bg-white hidden md:block text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize </button>
+            <button onClick={handleSummary} aria-label="Summarize blog content" className='bg-white hidden md:block text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize</button>

111-147: Consider extracting message rendering to a separate component.

The message rendering logic is quite complex. Consider extracting it to a separate component for better maintainability.

You could create a ChatMessage component that handles the rendering of individual messages, which would make this code more maintainable.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45d6eb0 and 26c3194.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • .env.example (1 hunks)
  • package.json (2 hunks)
  • src/app/blog/coderabbit-vs-others-ai-code-review-tools/page.mdx (1 hunks)
  • src/app/blog/enhancing-web-experiences-with-the-view-transitions-api/page.mdx (1 hunks)
  • src/app/blog/mongoDB-vs-postgreSQL/page.mdx (1 hunks)
  • src/app/blog/neon-vs-supabase/page.mdx (1 hunks)
  • src/app/blog/state-of-databases-2024/page.mdx (1 hunks)
  • src/app/blog/stream-vs-twilio/page.mdx (1 hunks)
  • src/app/blog/supabase-vs-clerk/page.mdx (1 hunks)
  • src/app/globals.css (1 hunks)
  • src/app/layout.tsx (2 hunks)
  • src/components/BlogPostClient.tsx (1 hunks)
  • src/components/ChatSuggestions.tsx (1 hunks)
  • src/components/blog/BlogChatInterface.tsx (1 hunks)
  • src/components/blog/BlogHeader.tsx (2 hunks)
  • src/components/blog/CommentSection.tsx (1 hunks)
  • tailwind.config.ts (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/components/blog/CommentSection.tsx
  • src/app/blog/neon-vs-supabase/page.mdx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/app/layout.tsx
  • src/app/globals.css
  • .env.example
🧰 Additional context used
🧠 Learnings (1)
src/components/ChatSuggestions.tsx (3)
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:16-27
Timestamp: 2024-12-06T20:47:07.465Z
Learning: In the `ChatSuggestions` component, keyboard accessibility for suggestions is intentionally not implemented because there are only two questions displayed, and the 'Enter' key is reserved for sending out user questions.
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:19-24
Timestamp: 2024-12-06T20:37:00.154Z
Learning: Keyboard accessibility was not added to `ChatSuggestions.tsx` because it displays only two questions.
Learnt from: Rahulsoni9321
PR: tyaga001/devtoolsacademy#49
File: src/components/ChatSuggestions.tsx:19-24
Timestamp: 2024-12-06T20:37:00.154Z
Learning: In `ChatSuggestions.tsx`, clicking a suggested question replaces the component with `ChatInterface.tsx`, which handles the loading state.
🪛 LanguageTool
src/app/blog/enhancing-web-experiences-with-the-view-transitions-api/page.mdx

[uncategorized] ~22-~22: Loose punctuation mark.
Context: ...ing user engagement and performance." } author={"Jitendra Nirnejak"} publis...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (25)
src/app/blog/coderabbit-vs-others-ai-code-review-tools/page.mdx (1)

15-16: Enhance Metadata Consistency:
The addition of the description prop to the BlogHeader component enriches the blog post’s metadata, providing important context for both readers and SEO. Please ensure that this description remains consistent with the metadata defined via getMetadata.

src/app/blog/stream-vs-twilio/page.mdx (1)

15-17: Improved Blog Metadata:
Introducing the description prop in the BlogHeader component clearly communicates the post’s focus on comparing Twilio and Stream. This change not only benefits SEO but also gives readers a concise overview of the content.

src/app/blog/state-of-databases-2024/page.mdx (1)

19-20: Updated Context with Description:
Adding the description prop here helps set the reader’s expectations by summarizing the post’s theme ("My thoughts on the state of Databases for Serverless"). This is a valuable enhancement for metadata clarity and overall user experience.

src/app/blog/mongoDB-vs-postgreSQL/page.mdx (1)

20-23: Improved Metadata Integration:
Integrating the description prop into the BlogHeader component enhances the clarity of the blog’s context. The detailed description ("A technical comparison of MongoDB vs. PostgreSQL to help you choose the best database solution for your SaaS") is consistent with similar updates across the blog posts and will positively impact SEO.

src/components/blog/BlogHeader.tsx (4)

13-13: Description property added to enhance blog metadata

Good addition of the description property to the BlogHeaderProps interface. This allows for more comprehensive blog metadata, improving context for both users and AI interactions.


20-20: Description prop properly destructured

Correctly destructured the new description prop from the component props, maintaining consistency with the interface definition.


61-62: Improved button styling and clearer label

The button label has been changed from "Chat with Claude AI" to "Summarize with AI", which better represents its functionality. The styling has also been enhanced with animation effects (shimmer) and a more refined border/background.


69-70: Enhanced BlogChatInterface with additional context

Good addition of blog metadata (blogDescription and blogTitle) to the BlogChatInterface component. This provides more context for the AI to generate relevant responses.

src/app/blog/supabase-vs-clerk/page.mdx (1)

15-17: Added description prop to BlogHeader

The description prop has been correctly implemented in this blog page, matching the metadata description defined in lines 8-9. This ensures consistency between SEO metadata and UI components.

src/components/ChatSuggestions.tsx (3)

4-8: Well-structured interface for ChatSuggestions component

The BlogChatSuggestionProps interface is clearly defined with appropriate types for all properties. The handleSend function type signature allows for optional custom prompts.


12-14: Responsive heading with proper accessibility

The heading uses responsive text sizing (text-lg for mobile, text-2xl for desktop) and clearly communicates the purpose of the component to users.


14-21: Responsive suggestion grid with appropriate hover effects

The suggestion containers use a responsive grid layout (single column on mobile, two columns on desktop) and have well-designed hover effects for better user interaction. Each suggestion clearly communicates its purpose.

Note: Based on previous discussions captured in the learnings, keyboard accessibility was intentionally omitted as the Enter key is reserved for sending user questions, which is an acceptable design decision given the component's context.

tailwind.config.ts (4)

43-51: Added border radius and font family configurations

Good addition of border radius variables using CSS custom properties for consistent styling across the application. The font family configuration ensures typography consistency.


52-97: Comprehensive keyframes animations added

Excellent addition of various animation keyframes including shimmer, fade-in variants, and pulse-shadow. These animations enhance the user interface with subtle visual feedback.


98-104: Animation utilities configured with appropriate timing

Well-configured animation utilities that reference the keyframes with appropriate durations and easing functions. The shimmer animation is particularly well-suited for loading states and interactive elements.


105-142: Comprehensive typography configuration

The typography configuration is thorough and uses HSL color variables for consistency with the theme. Both default and inverted variants are included, with appropriate styling for links, headings, and other text elements.

package.json (1)

26-30:

❓ Verification inconclusive

Verify compatibility of new AI and UI dependencies.

The addition of Google's Generative AI and new UI components looks good. However, ensure these are compatible with your existing dependencies and that you have proper documentation for their usage.


🏁 Script executed:

#!/bin/bash
# Check for potential dependency conflicts or vulnerabilities
npm audit
npm ls @google/generative-ai

Length of output: 122


Action: Verify Integration of New Dependencies
The new AI and UI dependencies are correctly added in package.json and npm audit reported no vulnerabilities. However, the output of npm ls @google/generative-ai did not confirm its installation in the dependency tree. Please manually verify that this dependency is properly installed and integrated—ensuring it is compatible with existing packages—and confirm that its usage is documented appropriately.

src/components/blog/BlogChatInterface.tsx (8)

15-20: Good job enhancing the interface with blog context.

Adding blogTitle and blogDescription to the interface improves the context available to the chat feature, which should enhance the user experience.


34-46: Excellent error handling improvement.

Good job implementing proper input validation with clear user feedback using toast notifications.


62-68: Well-implemented response error handling.

The error handling for API responses is well-implemented, checking for response.ok and providing meaningful error messages.


73-82: Good error type narrowing.

Nice use of type narrowing with instanceof Error to provide more specific error messages when possible.


112-113: Good use of conditional rendering with ChatSuggestions.

Showing chat suggestions when there are no messages is a good UX improvement, especially with the addition of context from the blog title and description.


172-178: Good accessibility improvements.

Adding aria-label, role, and aria-placeholder attributes improves accessibility for screen readers.


206-218: Good content parsing with security considerations.

The parseContent function correctly sanitizes content using DOMPurify to prevent XSS attacks. Good implementation of formatting for bold text and paragraphs.


153-163: Nice loading state indicator.

Good implementation of a loading state that clearly shows the user that the AI is processing their request.

@Rahulsoni9321
Copy link
Author

@Rahulsoni9321 , can you please pull the latest changes and resolve the conflicts?

Hey @nirnejak, resolved all the conflicts and committed the latest changes.
Please have a look.

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 (1)
src/components/blog/BlogChatInterface.tsx (1)

181-181: Consider making the Summarize button accessible on mobile.

The Summarize button is hidden on mobile screens (hidden md:block), which removes this useful functionality for mobile users.

-<button onClick={handleSummary} className='bg-white hidden md:block text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize </button>
+<button onClick={handleSummary} className='bg-white text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize</button>

Alternatively, consider adding a smaller icon-only button for mobile:

-<button onClick={handleSummary} className='bg-white hidden md:block text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize </button>
+<button onClick={handleSummary} className='bg-white hidden md:flex text-xs text-black hover:bg-white/60 rounded px-4 border border-black'>Summarize</button>
+<button onClick={handleSummary} className='bg-white md:hidden flex items-center justify-center p-2 text-black hover:bg-white/60 rounded border border-black' aria-label="Summarize blog"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><line x1="10" y1="9" x2="8" y2="9"/></svg></button>
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26c3194 and d82336a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • package.json (3 hunks)
  • src/components/ChatSuggestions.tsx (1 hunks)
  • src/components/blog/BlogChatInterface.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/ChatSuggestions.tsx
  • package.json
🔇 Additional comments (9)
src/components/blog/BlogChatInterface.tsx (9)

1-8: Good use of imports and hooks for enhanced functionality!

The imports bring in necessary components and utilities for enhanced chat functionality, including icons, animation libraries, and security tools like DOMPurify for XSS protection.


16-21: Well-structured interface additions for contextual chat experience.

The BlogChatInterfaceProps interface has been appropriately extended with blogTitle and optional blogDescription, enabling the chat to provide context-aware suggestions and responses.


35-47: Good error handling with user feedback.

The improved input validation with toast notifications provides clear feedback to users when they attempt to send empty messages.


63-83: Robust error handling implementation.

The error handling logic properly catches both API errors and unexpected errors, with appropriate type checking using instanceof Error. This ensures users always receive informative feedback rather than silent failures.


88-90: Useful shortcut for blog summarization.

The handleSummary function provides a convenient way for users to get a quick summary of the blog content with a predefined prompt.


112-114: Good UX with contextual suggestions.

When no messages exist, showing the ChatSuggestions component that utilizes the blog title and description creates a more engaging and helpful starting point for users.


115-148: Well-designed message display with visual distinction between roles.

The message display implementation:

  1. Clearly distinguishes between human and AI messages with different styling
  2. Uses appropriate icons for each message type
  3. Implements smooth animations for a polished UX
  4. Safely renders HTML content from AI responses
  5. Includes responsive design adjustments

173-180: Improved accessibility for input field.

The addition of accessibility attributes (aria-label, role, aria-placeholder) improves usability for screen readers and follows best practices.


207-219: Good implementation of content parsing with security measures.

The parseContent function effectively formats AI responses by:

  1. Converting markdown-style bold text to HTML
  2. Creating proper paragraph structure
  3. Using DOMPurify to sanitize the HTML and prevent XSS attacks

This is a secure approach to handling potentially unsafe content from external sources.

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