-
Notifications
You must be signed in to change notification settings - Fork 16
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
base: main
Are you sure you want to change the base?
Feature/chatbot error handling #49
Conversation
…acting with the chatbot.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
@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. |
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
npm warn config production Use WalkthroughThis 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
Sequence DiagramsequenceDiagram
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
Possibly Related PRs
Suggested Labels
Suggested Reviewers
📜 Recent review detailsConfiguration used: .coderabbit.yaml ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this 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 missingThe 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 longThe
TOAST_REMOVE_DELAY
is set to1000000
milliseconds (approximately 16.67 minutes), which may be longer than intended for a toast notification. Consider reducing it to a more typical duration, such as3000
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 experienceThe
TOAST_LIMIT
is set to1
, 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 numbersrc/components/BlogChatInterface.tsx (1)
79-82
: Remove unnecessary 'async' keyword from 'handleSummary' functionThe
handleSummary
function is declared asasync
but does not useawait
, making theasync
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 stylingThe 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 handleSendThe 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 linesMultiple consecutive empty lines should be removed to maintain code cleanliness.
package.json (2)
27-27
: Consider consolidating toast librariesYou've added both
@radix-ui/react-toast
andsonner
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 securityFor 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 buttonFor 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 linesThere are multiple consecutive empty lines that should be removed to maintain code cleanliness.
Line range hint
1-1
: Consider restructuring PR scopeThe current changes appear to mix styling updates with error handling functionality. Consider:
- Separating pure styling changes into a different PR
- Including documentation on how these UI changes support error handling
- Adding error-specific styling variations (colors, animations) if these are meant for error states
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
⛔ 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:
- Moving these styling changes to a separate PR
- 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:
- The animation timing (2s) is appropriate for error state feedback
- 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
There was a problem hiding this 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 keyThe 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 constantThe 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 messagesThe 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 structureConsider 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 requestsFor 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 supportThe 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 feedbackThe 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 increasingTOAST_LIMIT
The
TOAST_LIMIT
is currently set to1
, 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 theTOAST_REMOVE_DELAY
durationThe
TOAST_REMOVE_DELAY
is set to1000000
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 IDsThe
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
📒 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.
Hey @tyaga001 reviewed all the suggestions made by @coderabbitai and implented it along with all the updates. |
@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? ✅ Actions performedReview triggered.
|
Hey @Rahulsoni9321, can you please pull from |
There was a problem hiding this 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 consistencyThe 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 formattingThere 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 validationThe 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 functionSince 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);
withconst·date·=·new·Date(dateString)
(prettier/prettier)
[error] 37-37: Replace
····return·date.toLocaleDateString('en-US',·{·year:·'numeric',·month:·'long',·day:·'numeric'·});
withreturn·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 performanceThe 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"
withshimmer
(prettier/prettier)
[error] 60-60: Replace
"backgroundPosition":·"0·0"
withbackgroundPosition:·"0·0",
(prettier/prettier)
[error] 63-63: Replace
"backgroundPosition":·"-200%·0"
withbackgroundPosition:·"-200%·0",
(prettier/prettier)
[error] 64-64: Insert
,
(prettier/prettier)
src/components/BlogChatInterface.tsx (2)
194-206
: Optimize content parsing performanceThe 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 effectThe 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);
withconst·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
⛔ 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
:
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
:
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)
There was a problem hiding this 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 clarityThe 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 validationConsider 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 structureConsider 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 propertiesConsider 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 failuresConsider 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 accessibilityAdd 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 capabilitiesConsider 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
📒 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
:
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)
Hey @nirnejak , |
Hey @nirnejak resolved all the conflicts and made the chat interface responsive. Please let me know if anything else is needed. |
There was a problem hiding this 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 colorsThe
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 parameterThe
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'
withconst·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 styleUpdate 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
⛔ 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 thedescription
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
: Destructuringdescription
.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 beundefined
, 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
: Passingdescription
toBlogChatInterface
.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 propdescription
passed toBlogHeader
.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 inBlogHeader
.src/components/ChatSuggestions.tsx (1)
4-8
: Interface definition forBlogChatSuggestionProps
.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 ofBlogPostClientProps
.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
: EnsuringblogContent
,blogDescription
, andblogTitle
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, whileflex-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 handlingThe 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 2Length 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 2Length 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.tsxLength of output: 20728
@Rahulsoni9321 there seems to be some conflicts, can you please check? |
Hey @nirnejak, I have resolved all the conflicts, Please have a look. |
Summary by CodeRabbit
New Features
BlogPostClient
component for displaying blog details.Dependencies
Improvements
UI/UX Changes