feat(ui): add syntax highlighted code blocks with theme support and c…#68
Conversation
✅ Deploy Preview for develop-devlovers ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
WalkthroughThe changes consolidate Q&A-related components by moving AccordionList and Pagination from a shared directory to a dedicated q&a subdirectory, and introduce a new CodeBlock component with syntax highlighting and copy functionality powered by prism-react-renderer. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
frontend/components/q&a/CodeBlock.tsx (2)
19-23: Add error handling for clipboard API.The
navigator.clipboardAPI requires a secure context (HTTPS) and user permissions. Consider adding error handling to gracefully handle cases where clipboard access is denied or unavailable.🔎 Proposed improvement
const handleCopy = async () => { - await navigator.clipboard.writeText(code); - setCopied(true); - setTimeout(() => setCopied(false), 1500); + try { + await navigator.clipboard.writeText(code); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch (error) { + console.error('Failed to copy code:', error); + // Optionally show error state to user + } };
28-28: Avoid type assertion; use proper Language type.The
as anytype assertion bypasses TypeScript's type safety. Theprism-react-rendererlibrary exports aLanguagetype that should be used here.🔎 Proposed improvement
-import { Highlight, themes } from 'prism-react-renderer'; +import { Highlight, themes, Language } from 'prism-react-renderer'; import { useTheme } from 'next-themes'; import { useState } from 'react'; import { Copy, Check } from 'lucide-react'; type Props = { code: string; - language?: string | null; + language?: Language | null; };Then use:
code={code.trim()} - language={(language ?? 'markup') as any} + language={(language ?? 'markup') as Language} theme={theme}
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (5)
frontend/app/[locale]/q&a/page.tsx(1 hunks)frontend/components/q&a/AccordionList.tsx(3 hunks)frontend/components/q&a/CodeBlock.tsx(1 hunks)frontend/components/q&a/TabsSection.tsx(1 hunks)frontend/package.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/components/q&a/CodeBlock.tsx (1)
frontend/components/shop/theme-provider.tsx (1)
useTheme(15-27)
🔇 Additional comments (5)
frontend/app/[locale]/q&a/page.tsx (1)
3-3: LGTM!The import path update correctly reflects the reorganization of the TabsSection component into the q&a subdirectory.
frontend/components/q&a/AccordionList.tsx (1)
117-118: Delegation to CodeBlock component is well-structured.The refactoring correctly delegates code block rendering to the dedicated
CodeBlockcomponent, passing through the content and language metadata appropriately. This improves separation of concerns and enables consistent syntax highlighting across the application.Also applies to: 152-153
frontend/components/q&a/TabsSection.tsx (1)
8-9: LGTM!The import path updates correctly reflect the reorganization of AccordionList and Pagination into the q&a subdirectory, maintaining the existing functionality.
frontend/package.json (1)
44-44: No changes needed. Version 2.4.1 is the latest version available and no direct vulnerabilities have been found for this package.frontend/components/q&a/CodeBlock.tsx (1)
26-74: Confirm the Highlight component imports and theme usage match prism-react-renderer v2.x patterns: import Highlight without defaultProps, and use themes from "prism-react-renderer" (e.g.,themes.github) rather than importing individual theme files. The render prop usage withgetLinePropsandgetTokenPropsis v2-compatible.
| AccordionContent, | ||
| } from '@/components/ui/accordion'; | ||
|
|
||
| import CodeBlock from '@/components/q&a/CodeBlock'; |
There was a problem hiding this comment.
Critical: Naming collision between imported component and type definition.
The imported CodeBlock component name conflicts with the CodeBlock type defined at lines 21-25. This will cause TypeScript compilation errors.
🔎 Proposed fix: Rename the type to avoid collision
-import CodeBlock from '@/components/q&a/CodeBlock';
+import CodeBlockComponent from '@/components/q&a/CodeBlock';And update usages at lines 117 and 152:
- <CodeBlock key={index} code={block.content} language={block.language} />
+ <CodeBlockComponent key={index} code={block.content} language={block.language} />- <CodeBlock key={i} code={child.content} language={child.language} />
+ <CodeBlockComponent key={i} code={child.content} language={child.language} />Alternatively, rename the type instead:
-type CodeBlock = {
+type CodeBlockData = {
type: 'code';
language: string | null;
content: string;
};And update all type references (lines 37, 68, 115).
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In frontend/components/q&a/AccordionList.tsx around lines 11 and 21–25, the
imported component CodeBlock collides with a local type named CodeBlock; rename
the type (e.g., CodeBlockType or AccordionCodeBlock) and update all references
to that type (notably at lines ~37, ~68, ~115 and the places mentioned in the
review around 117 and 152) so the imported component name remains CodeBlock and
the type name is unique throughout the file.
| const { resolvedTheme } = useTheme(); | ||
| const [copied, setCopied] = useState(false); | ||
|
|
||
| const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight; |
There was a problem hiding this comment.
Potential hydration mismatch with next-themes.
The resolvedTheme from next-themes can be undefined during the initial server render and first client render, which may cause a hydration mismatch or flash of unstyled content. Consider providing a fallback or using mounted state from next-themes.
🔎 Proposed fix: Handle undefined resolvedTheme
export default function CodeBlock({ code, language }: Props) {
- const { resolvedTheme } = useTheme();
+ const { resolvedTheme, systemTheme } = useTheme();
const [copied, setCopied] = useState(false);
- const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight;
+ // Use systemTheme as fallback during hydration
+ const currentTheme = resolvedTheme ?? systemTheme ?? 'light';
+ const theme = currentTheme === 'dark' ? themes.nightOwl : themes.vsLight;Alternatively, you can suppress hydration warnings if the visual difference is acceptable:
<pre
- className={`${className} relative rounded-lg p-4 text-sm overflow-x-auto`}
+ className={`${className} relative rounded-lg p-4 text-sm overflow-x-auto`}
+ suppressHydrationWarning
style={style}
>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { resolvedTheme } = useTheme(); | |
| const [copied, setCopied] = useState(false); | |
| const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight; | |
| export default function CodeBlock({ code, language }: Props) { | |
| const { resolvedTheme, systemTheme } = useTheme(); | |
| const [copied, setCopied] = useState(false); | |
| // Use systemTheme as fallback during hydration | |
| const currentTheme = resolvedTheme ?? systemTheme ?? 'light'; | |
| const theme = currentTheme === 'dark' ? themes.nightOwl : themes.vsLight; |
🤖 Prompt for AI Agents
In frontend/components/q&a/CodeBlock.tsx around lines 14 to 17, resolvedTheme
from next-themes can be undefined on initial SSR/first render causing hydration
mismatches; change the logic to guard against undefined by either using the
mounted flag from useTheme (only compute/select theme after mounted is true) or
provide a safe fallback (e.g., treat undefined as 'light') so theme selection is
deterministic between server and client — implement the mounted check or default
value and ensure theme is derived only after mounted or from the fallback.
feat(ui): add syntax highlighted code blocks with theme support and c…
Add syntax highlighted code blocks with theme support and copy button
Overview
This pull request enhances code block rendering by introducing syntax highlighting, theme-aware color schemes, and a copy-to-clipboard button.
The implementation is built on top of
prism-react-rendererand fully supports light, dark, and system themes vianext-themes.Features
CodeBlockcomponentImplementation Details
prism-react-renderer(v2 API)<pre><code>rendering with a sharedCodeBlockcomponentvsLightfor light modenightOwlfor dark modeCopied)dangerouslySetInnerHTMLSummary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.