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 13 commits into
base: main
Choose a base branch
from

Conversation

Rahulsoni9321
Copy link

@Rahulsoni9321 Rahulsoni9321 commented Dec 6, 2024

Summary by CodeRabbit

  • New Features

    • Integrated Google Generative AI for blog content summarization.
    • Added toast notification system for improved user feedback.
    • Introduced AI-powered chat suggestions for blog posts.
    • Enhanced blog chat interface with more context and error handling.
    • New BlogPostClient component for displaying blog details.
  • Dependencies

    • Added new libraries for AI generation, toast notifications, and content parsing.
    • Installed Google Generative AI, Radix UI Toast, DOMPurify, and Sonner.
  • Improvements

    • Updated API endpoint to use Google Generative AI.
    • Added custom scrollbar styles.
    • Improved error handling and user interaction in chat interface.
    • Enhanced Tailwind CSS configuration for animations and typography.
    • Introduced new environment variable for API key configuration.
  • UI/UX Changes

    • Updated blog post interaction button to "Summarize with AI."
    • Added animated chat suggestions.
    • Implemented more informative toast notifications.
    • Enhanced overall layout and visual presentation of components.

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

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 must provide string spec
npm error A complete log of this run can be found in: /root/.npm/_logs/2025-01-13T21_02_14_231Z-debug-0.log

Walkthrough

This pull request introduces a comprehensive update to the blog chat interface by integrating Google's Generative AI (Gemini) instead of the previous Anthropic API. The changes span multiple files, including adding a new environment variable for the Gemini API key, updating dependencies, modifying the summarization API route, and implementing a new toast notification system. The modifications enhance the chat interface with improved error handling, sanitization, and user interaction features.

Changes

File Change Summary
.env.example Added GEMINI_API_KEY environment variable
package.json Added new dependencies: @google/generative-ai, @heroicons/react, @radix-ui/react-toast, @types/dompurify, dompurify, framer-motion, geist, highlight.js, html-react-parser, rehype-highlight, sonner; downgraded some existing dependencies
src/app/api/summarize/route.ts Replaced Anthropic API integration with Google Generative AI, improved error handling and input validation
src/app/globals.css Added custom scrollbar styles for .ChatArea and styles for .section-divider
src/app/layout.tsx Integrated Toaster component
src/components/BlogChatInterface.tsx Updated props, improved error handling, and added new methods for handling chat interactions
src/components/BlogPostClient.tsx Expanded props for BlogChatInterface, modified button text and styling
src/components/ChatSuggestions.tsx Introduced new component for chat suggestions
src/components/Navbar.tsx Updated button styles for GitHub and Sponsor buttons
src/components/ui/toast.tsx Added toast notification system components
src/components/ui/toaster.tsx Created Toaster component for managing toast notifications
src/hooks/use-toast.ts Implemented custom toast notification system with state management
tailwind.config.ts Added shimmer animation and extended theme configuration
.vscode/settings.json Added TypeScript React file formatter configuration
src/app/blog/[slug]/page.tsx Updated BlogHeader to include new description prop
src/components/BlogHeader.tsx Added description prop to BlogHeaderProps interface and updated button text

Sequence Diagram

sequenceDiagram
    participant User
    participant BlogChatInterface
    participant SummarizeAPI
    participant GoogleGenerativeAI

    User->>BlogChatInterface: Sends message/summary request
    BlogChatInterface->>SummarizeAPI: Forward request with content
    SummarizeAPI->>GoogleGenerativeAI: Generate content
    GoogleGenerativeAI-->>SummarizeAPI: Return generated response
    SummarizeAPI-->>BlogChatInterface: Send API response
    BlogChatInterface->>User: Display AI response
Loading

Possibly Related PRs

  • Update BlogPost component and enhance metadata handling in layout.tsx #9: The main PR introduces a new environment variable GEMINI_API_KEY, which is also referenced in the modifications to the BlogPost component in the retrieved PR, indicating a connection in the context of API integration.
  • Blog Improvements #58: The changes in the package.json file in the main PR, which include updates to dependencies, are relevant to the enhancements made in the Blog Improvements PR, as both involve modifications to the project's dependencies and overall structure.

Suggested Labels

enhancement, UX, accessibility, technical, frontend, optimization, ui

Suggested Reviewers

  • tyaga001

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 867e98c and 45d6eb0.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • src/app/blog/[slug]/page.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/blog/[slug]/page.tsx

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. (Beta)
  • @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

src/app/blog/[slug]/page.tsx Outdated Show resolved Hide resolved
src/app/api/summarize/route.ts Outdated Show resolved Hide resolved
src/app/api/summarize/route.ts Show resolved Hide resolved
src/hooks/use-toast.ts Outdated Show resolved Hide resolved
src/components/ui/toast.tsx Outdated Show resolved Hide resolved
src/components/BlogChatInterface.tsx Outdated Show resolved Hide resolved
src/components/BlogChatInterface.tsx Outdated Show resolved Hide resolved
src/components/ui/toaster.tsx Show resolved Hide resolved
src/components/ChatSuggestions.tsx Show resolved Hide resolved
src/components/ChatSuggestions.tsx Outdated Show resolved Hide resolved
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.

src/components/BlogChatInterface.tsx Show resolved Hide resolved
@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)

src/app/api/summarize/route.ts Outdated Show resolved Hide resolved
src/components/BlogChatInterface.tsx Outdated Show resolved Hide resolved
src/components/BlogChatInterface.tsx Outdated Show resolved Hide resolved
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)

.vscode/settings.json Show resolved Hide resolved
src/app/api/summarize/route.ts Show resolved Hide resolved
@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.

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.

2 participants