-
Notifications
You must be signed in to change notification settings - Fork 535
[Portal] Add AI chat functionality with markdown support #7139
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
[Portal] Add AI chat functionality with markdown support #7139
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
WalkthroughThis change introduces a comprehensive AI chat feature to the portal app, including new UI components for chat, markdown rendering, code blocks with syntax highlighting and copy functionality, tooltips, tables, and clipboard utilities. It updates dependencies, reorganizes the header, enhances global styles, and adds new hooks and utility functions, while also making internal improvements to code block and documentation rendering logic. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Header
participant Router
participant ChatPage
participant Chat
participant getChatResponse (API)
participant AI Service
User->>Header: Clicks "Ask AI" button
Header->>Router: Navigate to /chat
Router->>ChatPage: Render ChatPage
ChatPage->>Chat: Render Chat component
User->>Chat: Types message and sends
Chat->>getChatResponse: Call getChatResponse(userMessage, sessionId)
getChatResponse->>AI Service: POST message to AI endpoint
AI Service-->>getChatResponse: Returns response and conversationId
getChatResponse-->>Chat: Returns data
Chat->>User: Renders assistant reply in chat UI
Possibly related PRs
Warning Review ran into problems🔥 ProblemsErrors were encountered while retrieving linked issues. Errors (1)
📜 Recent review detailsConfiguration used: CodeRabbit UI ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (29)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (27)
⏰ Context from checks skipped due to timeout of 90000ms (5)
✨ Finishing Touches
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. 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
CodeRabbit Configuration File (
|
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
size-limit report 📦
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #7139 +/- ##
=======================================
Coverage 55.68% 55.68%
=======================================
Files 904 904
Lines 58340 58340
Branches 4107 4107
=======================================
Hits 32484 32484
Misses 25751 25751
Partials 105 105
🚀 New features to boost your workflow:
|
66942b0
to
02b6289
Compare
02b6289
to
f3255d2
Compare
f3255d2
to
1f2bb78
Compare
5eb526e
to
e6bdbf7
Compare
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: 12
🔭 Outside diff range comments (1)
apps/portal/src/components/Document/Code.tsx (1)
38-43
: 🛠️ Refactor suggestionFix language normalization order.
The current implementation applies normalization after the default assignment, which means
"shell"
or"sh"
values won't be normalized if the originallang
was falsy.- let lang = props.lang || "javascript"; + let lang = props.lang; + // Normalize specific language values first if (lang === "shell" || lang === "sh") { lang = "bash"; } + + // Apply default if still falsy + if (!lang) { + lang = "javascript"; + }
♻️ Duplicate comments (1)
apps/portal/src/app/references/components/TDoc/PageLayout.tsx (1)
120-120
:⚠️ Potential issueFix the incorrect spread operator usage in Set constructor.
This is the same syntax error previously identified. The spread operator should not be used when passing
slugs
to theSet
constructor, as it expects an iterable as a single argument, not individual elements.Apply this fix:
- return Array.from(new Set(...slugs)).map((slug) => { + return Array.from(new Set(slugs)).map((slug) => {
🧹 Nitpick comments (17)
apps/portal/src/hooks/useClipboard.ts (2)
5-8
: Address the TODO comment about SDK duplication.The comment suggests this hook is duplicated from the SDK. Consider importing it from the SDK instead to avoid code duplication and maintenance overhead.
Would you like me to help identify where this hook exists in the SDK and create a plan to import it instead?
17-19
: ESLint disable comment could be more specific.The disable comment mentions "legitimate usecase" but it's not clear why this specific useEffect usage requires disabling the rule. Consider being more specific about why this is necessary.
-// legitimate usecase -// eslint-disable-next-line no-restricted-syntax +// useEffect is needed here to manage timeout cleanup when hasCopied state changes +// eslint-disable-next-line no-restricted-syntaxapps/portal/src/components/ui/ScrollShadow/ScrollShadow.module.css (1)
19-19
: Define fallback for CSS custom property.The
--shadow
custom property is used but may not be defined in all contexts. Consider providing a fallback value.-background: linear-gradient(to bottom, var(--shadow), transparent); +background: linear-gradient(to bottom, var(--shadow, rgba(0, 0, 0, 0.1)), transparent);Apply similar changes to other gradient usages on lines 25, 30, and 35.
apps/portal/src/app/chat/page.tsx (1)
6-6
: Consider query client optimization for production.The query client is created at the module level, which means a new instance is created for each page render. For better performance and to maintain query cache across navigation, consider moving the query client to a higher level or using a singleton pattern.
-const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60 * 5, // 5 minutes + gcTime: 1000 * 60 * 30, // 30 minutes + }, + }, +});Alternatively, consider moving the QueryClient to the root layout if it should be shared across the entire app.
apps/portal/src/components/ui/inline-code.tsx (1)
3-6
: Consider expanding props interface for enhanced flexibility.The props interface is clean but could benefit from additional props for enhanced flexibility and reusability.
export function InlineCode({ code, className, + onClick, + ...props }: { code: string; className?: string; + onClick?: () => void; +} & React.HTMLAttributes<HTMLElement>) {This would allow for event handlers and other HTML attributes to be passed through, making the component more flexible for various use cases.
apps/portal/src/components/AI/api.ts (1)
31-34
: Consider validating the API response structure.The response is cast to a specific type without runtime validation. This could cause issues if the API response structure changes.
Consider adding response validation:
const data = (await response.json()) as { data: string; conversationId: string; }; + +// Validate response structure +if (!data.data || !data.conversationId) { + throw new Error("Invalid response structure from chat API"); +}apps/portal/src/components/code/getCodeHtml.tsx (1)
38-44
: Consider safer type casting for language parameter.The language is cast to
BundledLanguage
without validation. If an unsupported language is passed, this could cause issues with Shiki.Consider validating the language before casting:
+// List of common fallback languages supported by Shiki +const FALLBACK_LANGUAGES = ['javascript', 'typescript', 'text', 'plaintext']; + +const safeLang = lang && typeof lang === 'string' + ? lang + : 'javascript'; const html = await codeToHtml(formattedCode, { - lang: (lang || "javascript") as BundledLanguage, + lang: safeLang as BundledLanguage, themes: { light: "github-light", dark: "github-dark-default", }, });apps/portal/src/components/ui/textarea.tsx (1)
26-48
: Consider performance optimizations and bounds checking for auto-resize functionality.The auto-resize implementation has a few areas for improvement:
- Performance concern: Direct DOM manipulation on every value change could be expensive for large text inputs
- Missing bounds checking: No maximum height limit could lead to UI issues with very large content
- Dependency array: The biome-ignore comment suggests there might be a legitimate concern about the dependency array
Consider these improvements:
export function AutoResizeTextarea(props: TextareaProps) { const textareaRef = React.useRef<HTMLTextAreaElement>(null); + const maxHeight = 200; // Add configurable max height - // biome-ignore lint/correctness/useExhaustiveDependencies: value is needed in deps array React.useEffect(() => { const textarea = textareaRef.current; if (textarea) { textarea.style.height = "auto"; - textarea.style.height = `${textarea.scrollHeight}px`; + const newHeight = Math.min(textarea.scrollHeight, maxHeight); + textarea.style.height = `${newHeight}px`; } }, [props.value]); return ( <Textarea ref={textareaRef} {...props} style={{ ...props.style, overflowY: "hidden", + maxHeight: `${maxHeight}px`, }} /> ); }apps/portal/src/components/code/CodeBlockContainer.tsx (1)
19-19
: Consider renaming to avoid the need for variable aliasing.The comment indicates a naming conflict between the hook's
onCopy
and the prop'sonCopy
. Consider renaming the prop to be more descriptive and avoid the need for aliasing.export function CodeBlockContainer(props: { codeToCopy: string; children: React.ReactNode; className?: string; scrollableClassName?: string; scrollableContainerClassName?: string; copyButtonClassName?: string; shadowColor?: string; - onCopy?: (code: string) => void; + onCopyComplete?: (code: string) => void; }) { - const { hasCopied, onCopy: onClipboardCopy } = useClipboard(props.codeToCopy); + const { hasCopied, onCopy } = useClipboard(props.codeToCopy); // ... rest of component onClick={() => { - onClipboardCopy(); - props.onCopy?.(props.codeToCopy); + onCopy(); + props.onCopyComplete?.(props.codeToCopy); }}apps/portal/src/components/AI/chat.tsx (2)
63-249
: Consider breaking down this large component for better maintainability.The
Chat
component handles multiple responsibilities including message management, input handling, API calls, analytics, and rendering. Consider extracting some functionality into separate hooks or components.Potential extractions:
- Custom hook for chat logic: Extract message state, API calls, and conversation management
- Separate component for message list: Handle message rendering and auto-scrolling
- Separate component for input area: Handle input state and submission
Example structure:
// Custom hook function useChat() { // Extract state management and API logic return { messages, sendMessage, isLoading, ... }; } // Message list component function MessageList({ messages }: { messages: Message[] }) { // Handle rendering and auto-scroll logic } // Input component function ChatInput({ onSendMessage }: { onSendMessage: (message: string) => void }) { // Handle input state and submission }
88-89
: Potential race condition in message ID generation.Using
Date.now()
andDate.now() + 1
for generating message IDs could potentially create collisions if messages are sent very quickly in succession.Consider using a more robust ID generation approach:
- const userMessage: Message = { - id: Date.now().toString(), - role: "user", - content, - }; - - const loadingMessageId = (Date.now() + 1).toString(); + const userMessageId = crypto.randomUUID(); + const loadingMessageId = crypto.randomUUID(); + + const userMessage: Message = { + id: userMessageId, + role: "user", + content, + };apps/portal/src/app/Header.tsx (2)
224-233
: Add error handling for navigation.The router.push calls should handle potential navigation errors gracefully.
+ const handleAskAIClick = () => { + try { + router.push("/chat"); + } catch (error) { + console.error("Navigation to chat failed:", error); + } + }; <Button variant="primary" - onClick={() => { - router.push("/chat"); - }} + onClick={handleAskAIClick} > <BotIcon className="mr-2 size-4" /> Ask AI </Button>
252-258
: Consider consistent styling between mobile and desktop Ask AI buttons.The mobile version uses
variant="ghost"
while desktop usesvariant="primary"
. Consider if this difference is intentional for the user experience.If the styling difference is intentional, consider adding a comment explaining the design rationale. If not, standardize the button variants.
apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx (2)
50-56
: Consider making the scroll threshold configurable.The hardcoded threshold of
10
pixels works for most cases, but making it configurable would increase flexibility for different use cases.export function ScrollShadow(props: { children: React.ReactNode; className?: string; scrollableClassName?: string; disableTopShadow?: boolean; shadowColor?: string; shadowClassName?: string; + scrollThreshold?: number; }) { + const threshold = props.scrollThreshold ?? 10; // Later in the handleScroll function: - if (contentScrollHeight > 10) { + if (contentScrollHeight > threshold) { // ... rest of the logic - if (contentScrollWidth > 10) { + if (contentScrollWidth > threshold) {Also applies to: 59-66
69-73
: Optimize the double requestAnimationFrame pattern.The double
requestAnimationFrame
might be unnecessary and could introduce additional delay. Consider if a single frame is sufficient.- requestAnimationFrame(() => { - requestAnimationFrame(() => { - handleScroll(); - }); - }); + requestAnimationFrame(() => { + handleScroll(); + });apps/portal/src/components/markdown/MarkdownRenderer.tsx (2)
104-108
: Review the blanket external link behavior.All links are forced to open in a new tab (
target="_blank"
), which might not be desired for internal links or could hurt user experience. Consider checking if the link is external before adding this behavior.a: (props) => { + const isExternal = props.href?.startsWith('http') && !props.href.includes(window.location.hostname); return ( <Link href={props.href ?? "#"} - target="_blank" + target={isExternal ? "_blank" : undefined} + rel={isExternal ? "noopener noreferrer" : undefined} {...cleanedProps(props)} className="mt-4 text-blue-600 underline visited:text-purple-600 hover:text-blue-800" /> ); + },
120-120
: Improve the heuristic for code block detection.The current heuristic (className presence OR length > 80) might incorrectly classify some inline code as blocks or vice versa. Consider a more robust detection method.
- if (inheritedClassName || codeStr.length > 80) { + // More robust detection: check for language class first, then newlines, then length + const hasLanguageClass = inheritedClassName?.includes("language-"); + const hasNewlines = codeStr.includes('\n'); + const isLikelyBlock = hasLanguageClass || hasNewlines || codeStr.length > 120; + + if (isLikelyBlock) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (30)
apps/dashboard/package.json
(1 hunks)apps/portal/package.json
(2 hunks)apps/portal/src/app/Header.tsx
(4 hunks)apps/portal/src/app/chat/page.tsx
(1 hunks)apps/portal/src/app/globals.css
(3 hunks)apps/portal/src/app/layout.tsx
(1 hunks)apps/portal/src/app/references/components/TDoc/PageLayout.tsx
(1 hunks)apps/portal/src/app/references/components/TDoc/Summary.tsx
(1 hunks)apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts
(1 hunks)apps/portal/src/app/references/typescript/[version]/[[...slug]]/page.tsx
(1 hunks)apps/portal/src/components/AI/api.ts
(1 hunks)apps/portal/src/components/AI/chat.tsx
(1 hunks)apps/portal/src/components/Document/Code.tsx
(2 hunks)apps/portal/src/components/code/CodeBlockContainer.tsx
(1 hunks)apps/portal/src/components/code/RenderCode.tsx
(1 hunks)apps/portal/src/components/code/code.client.tsx
(1 hunks)apps/portal/src/components/code/getCodeHtml.tsx
(1 hunks)apps/portal/src/components/code/plaintext-code.tsx
(1 hunks)apps/portal/src/components/markdown/MarkdownRenderer.tsx
(1 hunks)apps/portal/src/components/others/DocSearch.tsx
(2 hunks)apps/portal/src/components/ui/CopyButton.tsx
(1 hunks)apps/portal/src/components/ui/LoadingDots.tsx
(1 hunks)apps/portal/src/components/ui/ScrollShadow/ScrollShadow.module.css
(1 hunks)apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx
(1 hunks)apps/portal/src/components/ui/inline-code.tsx
(1 hunks)apps/portal/src/components/ui/table.tsx
(1 hunks)apps/portal/src/components/ui/textarea.tsx
(1 hunks)apps/portal/src/components/ui/tooltip.tsx
(1 hunks)apps/portal/src/hooks/useClipboard.ts
(1 hunks)apps/portal/src/lib/useIsomorphicLayoutEffect.ts
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (6)
apps/portal/src/app/chat/page.tsx (1)
apps/portal/src/components/AI/chat.tsx (1)
Chat
(63-249)
apps/portal/src/components/ui/CopyButton.tsx (2)
apps/portal/src/hooks/useClipboard.ts (1)
useClipboard
(9-36)apps/portal/src/components/ui/tooltip.tsx (1)
ToolTipLabel
(32-70)
apps/portal/src/components/code/plaintext-code.tsx (1)
apps/portal/src/components/code/CodeBlockContainer.tsx (1)
CodeBlockContainer
(9-59)
apps/portal/src/components/ui/table.tsx (1)
apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx (1)
ScrollShadow
(8-151)
apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx (1)
apps/portal/src/lib/useIsomorphicLayoutEffect.ts (1)
useIsomorphicLayoutEffect
(3-4)
apps/portal/src/components/markdown/MarkdownRenderer.tsx (4)
apps/portal/src/components/code/plaintext-code.tsx (1)
PlainTextCodeBlock
(4-29)apps/portal/src/components/code/code.client.tsx (2)
CodeClient
(27-54)CodeLoading
(19-25)apps/portal/src/components/ui/inline-code.tsx (1)
InlineCode
(3-17)apps/portal/src/components/ui/table.tsx (7)
TableContainer
(151-151)Table
(143-143)TableHead
(147-147)TableCell
(149-149)TableHeader
(144-144)TableBody
(145-145)TableRow
(148-148)
⏰ Context from checks skipped due to timeout of 90000ms (6)
- GitHub Check: Unit Tests
- GitHub Check: Lint Packages
- GitHub Check: Size
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Analyze (javascript)
🔇 Additional comments (44)
apps/portal/src/app/references/typescript/[version]/[[...slug]]/page.tsx (1)
15-16
: Good temporary workaround while fixing the syntax error.Commenting out the
generateStaticParams
export is appropriate while the spread operator syntax error inPageLayout.tsx
line 120 is being resolved. This prevents runtime failures during static generation.Once the syntax error in
PageLayout.tsx
is fixed, remember to uncomment this export to restore static generation functionality.apps/dashboard/package.json (1)
92-92
:❓ Verification inconclusive
Verify react-markdown v10.1.0 compatibility.
This is a major version upgrade from v9 to v10. Please ensure that all existing markdown rendering functionality still works correctly, as major version updates often include breaking changes.
🏁 Script executed:
#!/bin/bash # Description: Check for react-markdown v10 breaking changes and usage patterns # Expected: Find any usage of react-markdown and verify compatibility # Search for react-markdown imports and usage rg -A 5 "react-markdown|ReactMarkdown" --type ts --type tsx --type js --type jsx # Search for any markdown-related components that might be affected ast-grep --pattern 'import $_ from "react-markdown"'Length of output: 407
Verify ReactMarkdown v10 Compatibility in MarkdownRenderer Components
ReactMarkdown v10 introduces breaking changes—props like
source
were replaced bychildren
, HTML handling moved to separate rehype plugins, and GitHub-flavored features now require theremark-gfm
plugin. Please review and update the following files to ensure rendering still works as expected:
- apps/portal/src/components/markdown/MarkdownRenderer.tsx
- apps/dashboard/src/components/contract-components/published-contract/markdown-renderer.tsx
Checklist for each file:
- Replace any
source
prop usage with<ReactMarkdown>{content}</ReactMarkdown>
.- Add
remark-gfm
if you rely on tables, strikethrough, task lists, etc.- If you render raw HTML, include and configure
rehype-raw
.- Confirm that any custom renderers or plugin integrations still operate correctly.
apps/portal/src/components/ui/ScrollShadow/ScrollShadow.module.css (1)
20-20
: Inconsistent opacity settings.The top shadow has
opacity: 0
while other shadows don't specify opacity. This seems inconsistent - is this intentional for the initial state?Please clarify if the
opacity: 0
on the top shadow is intentional for the initial state, and whether other shadows should have explicit opacity values for consistency.apps/portal/src/components/others/DocSearch.tsx (2)
138-138
: LGTM! Concise placeholder text.The shortened placeholder text "Search docs" is more concise while maintaining clarity.
360-360
: LGTM! Improved visual styling.The addition of
gap-6
provides better spacing between elements andtext-muted-foreground
ensures consistent color theming for the search button.apps/portal/src/lib/useIsomorphicLayoutEffect.ts (1)
1-4
: LGTM! Standard SSR-compatible layout effect hook.This implementation correctly handles the isomorphic rendering pattern by using
useLayoutEffect
in browser environments and falling back touseEffect
on the server side to prevent hydration warnings.apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts (1)
414-414
:✅ Verification successful
Verify consistency of typedoc data structure changes.
The change from accessing
children[0]?.value
todata?.hName
suggests an update in the typedoc data structure. Ensure this change is consistent across all locations where similar typedoc summary data is accessed.Run the following script to verify consistent usage of this new data access pattern:
🏁 Script executed:
#!/bin/bash # Description: Check for any remaining usage of the old data access pattern in typedoc components # Search for potential old patterns that might need updating rg -A 3 -B 3 "children\[0\]\.value" --type ts --type tsx # Search for the new pattern usage to verify consistency rg -A 3 -B 3 "data\.hName" --type ts --type tsxLength of output: 164
Let’s retry the search without relying on rg’s file-type filters:
🏁 Script executed:
#!/bin/bash # Search for the old pattern in all TS and TSX files rg -g '*.ts*' -n -C3 'children\[0\]\.value' # Search for the new pattern in all TS and TSX files rg -g '*.ts*' -n -C3 'data\.hName'Length of output: 82
No remaining old typedoc data access patterns detected.
I searched all
.ts
/.tsx
files and found no instances of the oldchildren[0].value
or the newdata.hName
patterns outside ofgetSidebarLinkgroups.ts
. This change is isolated and consistent—ready to merge.apps/portal/src/app/references/components/TDoc/Summary.tsx (1)
88-88
: LGTM! Consistent data access pattern with proper fallback.This change aligns with the typedoc data structure update seen in other files. The addition of the nullish coalescing operator (
?? ""
) provides a safe fallback for the slugger when the heading name is undefined, which is good defensive programming.apps/portal/package.json (5)
31-31
: LGTM: Tooltip dependency addition aligns with new UI components.The addition of
@radix-ui/react-tooltip
v1.2.3 is consistent with the new tooltip UI components mentioned in the AI summary.
49-49
: LGTM: React utilities dependency supports new functionality.The addition of
react-children-utilities
v2.10.0 appropriately supports enhanced React component manipulation needed for the new features.
53-53
: LGTM: remark-gfm upgrade aligns with react-markdown update.The upgrade of
remark-gfm
from v3.0.1 to v4.0.1 is consistent with the react-markdown upgrade and enables enhanced GitHub Flavored Markdown support.
52-52
:✅ Verification successful
Verify react-markdown v10.1.0 compatibility and security.
The upgrade to
react-markdown
v10.1.0 is a significant version jump that supports the new markdown rendering features. Ensure compatibility with existing markdown content and verify security implications.
🌐 Web query:
What are the breaking changes and security advisories for react-markdown v10.1.0?
💡 Result:
As of May 24, 2025, there are no reported breaking changes or security advisories for
react-markdown
version 10.1.0. The latest release, 10.1.0, was published on March 7, 2025, and includes the following updates:
Enhancements:
- Added a
fallback
prop toMarkdownHooks
to improve functionality.Fixes:
- Resolved a race condition in
MarkdownHooks
to enhance stability.For a comprehensive list of changes, you can refer to the full changelog. (sourceforge.net)
Regarding security,
react-markdown
is designed with safety in mind. By default, it filters out potentially dangerous protocols in link URIs to prevent XSS vulnerabilities. However, if you customize thetransformLinkUri
ortransformImageUri
functions, ensure they are implemented securely to maintain this protection. (medium.com)Additionally,
react-markdown
has no direct vulnerabilities reported in Snyk's database for version 10.1.0. (security.snyk.io)It's always advisable to stay updated with the latest releases and review the changelog for any new updates or fixes.
Citations:
- 1: https://sourceforge.net/projects/react-markdown.mirror/files/10.1.0/?utm_source=openai
- 2: https://medium.com/javascript-security/avoiding-xss-via-markdown-in-react-91665479900?utm_source=openai
- 3: https://security.snyk.io/package/npm/react-markdown/10.1.0?utm_source=openai
react-markdown v10.1.0 compatibility and security verified
We’ve confirmed that the upgrade to react-markdown 10.1.0 (published Mar 7, 2025) introduces no breaking changes or known vulnerabilities:
- Enhancements:
• Added afallback
prop toMarkdownHooks
.
• Fixed a race condition inMarkdownHooks
.- Security:
• Default URI handlers continue to filter out dangerous protocols to prevent XSS.
• No advisories reported in Snyk for v10.1.0.- Action: Proceed with this dependency version. If you override
transformLinkUri
ortransformImageUri
, ensure your implementations preserve sanitization.
23-24
:❓ Verification inconclusive
Verify MDX major version upgrade compatibility.
The upgrade from
@mdx-js/loader
and@mdx-js/react
from v2.3.0 to v3.1.0 represents a major version change that may introduce breaking changes. Ensure that existing MDX content and configurations are compatible with v3.x.
🏁 Script executed:
#!/bin/bash # Description: Check for potential breaking changes in MDX v3 usage across the codebase # Expected: Find MDX-related files and configurations to verify compatibility echo "Searching for MDX usage patterns..." rg -A 3 -B 3 "@mdx-js|\.mdx" --type js --type ts --type tsx --type json echo -e "\n\nSearching for MDX configuration files..." fd -e js -e ts -e json | xargs rg -l "mdx" echo -e "\n\nChecking for MDX imports and usage patterns..." ast-grep --pattern 'import $_ from "@mdx-js/$_"'Length of output: 1149
🏁 Script executed:
#!/bin/bash # Verify if any .mdx files exist and search for MDX imports in code. echo "Searching for .mdx files..." find . -type f -name '*.mdx' -print echo -e "\nSearching for MDX imports across JS/TS/JSX/TSX files..." rg -n '@mdx-js' -g '*.js' -g '*.jsx' -g '*.ts' -g '*.tsx'Length of output: 35382
Verify MDX v3 integration in your Next.js setup
Please double-check that your MDX loader/plugin configuration in
apps/portal/next.config.js
(or wherever you apply@next/mdx
/webpack rules) is updated for the v3 API. Although your.mdx
pages will still compile, MDX v3 introduced breaking-change renames and new option keys.Key points to verify:
- Update any
withMDX()
or webpack rule options from the v2 names (e.g.providerImportSource
) to the v3 equivalents (remarkPlugins
,rehypePlugins
, etc.).- Ensure you’re not relying on any removed exports in
@mdx-js/react
—if you wrap content withMDXProvider
, adjust imports per the v3 docs.- Run a few representative
.mdx
pages through your local dev build to confirm rendering and layout components still work as expected.apps/portal/src/components/ui/LoadingDots.tsx (1)
1-10
: Excellent implementation with good accessibility practices.The LoadingDots component is well-implemented with several strong points:
- Accessibility: Includes
sr-only
text for screen readers- Animation: Properly staggered bounce animations with negative delays create a smooth visual effect
- Styling: Consistent use of Tailwind utilities for size, color, and spacing
- Simplicity: Clean, focused component with a single responsibility
The component follows React and accessibility best practices effectively.
apps/portal/src/app/chat/page.tsx (1)
8-16
: LGTM: Well-structured chat page with proper layout.The ChatPage component is well-implemented with:
- Client directive: Appropriate for interactive chat functionality
- Query provider: Correctly wraps the Chat component to enable React Query
- Responsive layout: Uses appropriate Tailwind classes for different screen sizes
- Proper containment: Layout constraints ensure good UX on various devices
The component follows Next.js app directory conventions and provides a solid foundation for the chat interface.
apps/portal/src/components/ui/inline-code.tsx (1)
7-17
: LGTM: Well-styled inline code component.The InlineCode component is excellently implemented with:
- Semantic HTML: Uses proper
<code>
element- Comprehensive styling: Covers border, background, padding, font, and spacing
- Class merging: Allows customization via className prop
- Consistent design: Follows design system patterns with muted colors and proper contrast
- Typography: Appropriate font size and monospace font for code display
The component provides a solid foundation for inline code rendering in markdown content.
apps/portal/src/components/ui/CopyButton.tsx (1)
9-36
: LGTM! Well-implemented copy button component.The component follows React best practices with:
- Proper TypeScript typing
- Good accessibility with aria-label
- Clean conditional rendering for visual feedback
- Integration with tooltip system for better UX
- Flexible styling props
The 1000ms timeout for the copy feedback is a good UX choice.
apps/portal/src/components/code/getCodeHtml.tsx (2)
5-20
: LGTM! Clean language detection logic.The
isPrettierSupportedLang
function properly handles different JavaScript/TypeScript language identifiers and null/undefined values.
28-36
: Good error handling for Prettier formatting.The
.catch(() => code)
fallback ensures the function continues to work even if Prettier formatting fails, which is a good defensive programming practice.apps/portal/src/components/code/RenderCode.tsx (3)
32-36
: LGTM! Good UX implementation for copy functionality.The copy button implementation is well-designed:
- Positioned absolutely for clean layout
- Hidden by default, shown on hover (good UX)
- Proper z-index stacking
- Smooth opacity transition
- Appropriately sized for the context
19-26
: Good use of ScrollShadow component for enhanced UX.The integration with
ScrollShadow
provides visual feedback for scrollable content, and the responsive text sizing (text-xs md:text-sm
) ensures good readability across devices.
27-31
:❓ Verification inconclusive
Verify HTML content is properly sanitized to prevent XSS.
The component uses
dangerouslySetInnerHTML
to render code HTML. While the comment suggests this is intentional, ensure the HTML content fromgetCodeHtml
is safe and doesn't contain user-controlled content that could lead to XSS vulnerabilities.Since the HTML comes from the Shiki library via
getCodeHtml
, this should be safe as Shiki generates syntax-highlighted HTML from code strings. However, let's verify the HTML generation pipeline:
🏁 Script executed:
#!/bin/bash # Verify how HTML is generated and if user input is properly handled echo "Checking for HTML generation and sanitization in getCodeHtml..." ast-grep --pattern 'function getCodeHtml($_, $_) { $$$ }' echo "Checking for any other uses of dangerouslySetInnerHTML..." rg -A 3 "dangerouslySetInnerHTML" echo "Checking Shiki codeToHtml usage patterns..." rg -A 5 "codeToHtml"Length of output: 69869
Confirm Shiki-generated HTML is properly escaped before using dangerouslySetInnerHTML
I didn’t find any manual sanitization in the pipeline—RenderCode relies entirely on Shiki’s codeToHtml output. Before shipping, please verify that:
- apps/portal/src/components/code/RenderCode.tsx (lines 27–31)
uses dangerouslySetInnerHTML with the rawhtml
from getCodeHtml.- apps/portal/src/components/code/getCodeHtml.tsx
imports and invokes Shiki’scodeToHtml
; there’s no additional sanitization layer.- No other sanitizers or escapes are applied downstream of
codeToHtml
.Action items:
- Confirm via Shiki docs or tests that
codeToHtml
always HTML-escapes user code (no raw<script>
or attribute injection).- If any untrusted input could slip through, add an explicit sanitizer (e.g., DOMPurify) or switch to a safer rendering approach.
apps/portal/src/components/code/plaintext-code.tsx (1)
4-29
: LGTM! Clean and well-structured component.The implementation is clean and follows good practices:
- Proper prop forwarding to the container component
- Appropriate styling for code display with
whitespace-pre
- Good separation of concerns between the wrapper and container logic
apps/portal/src/components/code/CodeBlockContainer.tsx (1)
9-59
: Well-structured container component with good UX.The component provides excellent user experience with:
- Clear visual feedback for copy state
- Flexible styling options through props
- Proper integration with ScrollShadow for better readability
- Accessible button implementation
apps/portal/src/components/AI/chat.tsx (1)
77-80
: Good analytics implementation with PostHog integration.The PostHog event capture provides valuable insights into chat usage patterns and includes relevant context like session ID and message content.
apps/portal/src/components/code/code.client.tsx (2)
19-25
: LGTM: Clean loading component.The CodeLoading component provides a good user experience with appropriate minimum height and centered loading indicator.
10-17
: LGTM: Comprehensive prop types.The CodeProps interface is well-designed with appropriate optional properties for styling customization.
apps/portal/src/components/Document/Code.tsx (1)
31-31
: LGTM: Improved type flexibility.Expanding the language prop type to include
string | undefined | null
provides better flexibility for dynamic code rendering scenarios.apps/portal/src/app/globals.css (4)
44-52
: LGTM: Well-organized sidebar variables.The addition of sidebar-specific CSS custom properties follows good design system practices and provides consistent theming support.
125-137
: Excellent autofill styling fix.The autofill input styling fixes address a common UX issue where browser autofill overrides custom styling. The long transition duration is a clever workaround to effectively disable the autofill background.
139-151
: LGTM: Comprehensive scrollbar hiding utilities.The
.no-scrollbar
utilities properly handle scrollbar hiding across all major browsers (Chrome/Safari, Firefox, IE/Edge).
111-123
: Good shiki theme integration.The explicit color and font style overrides for shiki syntax highlighting ensure proper theme integration in both light and dark modes.
apps/portal/src/app/Header.tsx (2)
12-19
: LGTM: Clean imports and navigation setup.The addition of BotIcon and useRouter imports is appropriate for the new AI chat functionality.
309-315
: Good reorganization of Tools dropdown.Moving the Tools dropdown to its own container improves the header layout structure and maintains consistency with other dropdown sections.
apps/portal/src/components/ui/tooltip.tsx (3)
1-30
: Well-structured tooltip component setup.The basic tooltip components follow React best practices with proper forwardRef patterns and sensible default styling. The use of Radix UI primitives ensures good accessibility support.
42-44
: Good conditional rendering pattern.The early return when no label is provided prevents unnecessary tooltip rendering, which is an efficient approach.
49-49
:✅ Verification successful
Verify the necessity of
!pointer-events-auto
class.The
!pointer-events-auto
class with the!
important modifier seems unusual for a trigger element. This might interfere with normal pointer interactions or conflict with parent pointer-events settings.
🏁 Script executed:
#!/bin/bash # Search for pointer-events usage patterns in the codebase to understand the context rg -A 3 -B 3 "pointer-events" --type css --type tsx --type tsLength of output: 89
🏁 Script executed:
#!/bin/bash # Locate the tooltip component file FILE=$(fd -t f tooltip.tsx | head -n 1) echo "Inspecting file: $FILE" # Show the first 200 lines to see the component and class usage sed -n '1,200p' "$FILE"Length of output: 2554
🏁 Script executed:
#!/bin/bash # Search for all pointer-events utility classes in the repo echo "Looking for pointer-events classes..." rg -n "pointer-events-(none|auto)" . || true # Specifically check for usage of the override class rg -n "!pointer-events-auto" . || trueLength of output: 16790
Keep
!pointer-events-auto
on TooltipTrigger
This override is intentional: many of our UI components (e.g. buttons, selects) applydisabled:pointer-events-none
to block interaction when disabled. By using!pointer-events-auto
on theTooltipTrigger
(viaasChild
), we ensure the trigger still receives pointer events so tooltips appear even on disabled elements. This pattern is already used in multiple apps (portal, playground-web, dashboard) and should remain as is.apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx (3)
23-34
: Proper SSR-safe effect setup.The use of
useIsomorphicLayoutEffect
correctly handles server-side rendering scenarios where layout effects need to be deferred touseEffect
.
76-84
: Proper cleanup implementation.The cleanup function correctly removes event listeners and disconnects the ResizeObserver, preventing memory leaks.
47-48
:✅ Verification successful
Verify scroll calculation logic.
The scroll height and width calculations look correct, but it's worth double-checking the math for edge cases.
🏁 Script executed:
#!/bin/bash # Search for similar scroll calculation patterns in the codebase rg -A 5 -B 5 "scrollHeight.*offsetHeight|scrollWidth.*offsetWidth"Length of output: 4889
Scroll calculation logic verified
I searched across the codebase and found the same
scrollHeight – offsetHeight
andscrollWidth – offsetWidth
calculations in four instances:
- apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx
- apps/portal/src/components/others/ScrollShadow/ScrollShadow.tsx
- apps/playground-web/src/components/ui/ScrollShadow/ScrollShadow.tsx
- apps/dashboard/src/@/components/ui/ScrollShadow/ScrollShadow.tsx
Subtracting the container’s visible dimension (wrapper.offsetHeight/offsetWidth) from the total content size (content.scrollHeight/scrollWidth) correctly yields the maximum scrollable distance. No edge-case issues were identified, so no changes are required.
apps/portal/src/components/ui/table.tsx (2)
6-119
: Well-implemented table component suite.All table components follow consistent patterns with proper forwardRef implementation, TypeScript typing, and sensible default styling. The use of
cn
for conditional class names maintains flexibility.
121-140
: Excellent ScrollShadow integration.The TableContainer properly integrates with ScrollShadow, using appropriate props like
disableTopShadow
for tables and setting a high z-index for shadows to ensure proper layering.apps/portal/src/components/markdown/MarkdownRenderer.tsx (2)
21-27
: Good helper function for prop cleaning.The
cleanedProps
function properly removes thenode
prop to prevent React warnings while maintaining type safety.
171-191
: Excellent table integration.The table rendering properly integrates with the custom table components and uses TableContainer for scroll shadow functionality. The component mapping is comprehensive and maintains consistent styling.
e6bdbf7
to
50096cd
Compare
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
♻️ Duplicate comments (3)
apps/portal/src/components/markdown/MarkdownRenderer.tsx (3)
57-101
: 🛠️ Refactor suggestionSEO and accessibility concern: Heading hierarchy disruption persists.
The implementation still shifts all heading levels down by one (h1→h2, h2→h3, etc.) and renders h6 as a paragraph, which breaks semantic heading hierarchy and negatively impacts SEO and screen reader navigation.
Consider maintaining the original heading hierarchy or adding a prop to control this behavior:
h1: (props) => ( - <h2 + <h1 className={cn( commonHeadingClassName, "mb-3 border-border border-b pb-2 text-xl md:text-2xl", )} {...cleanedProps(props)} /> ),
32-35
:⚠️ Potential issueCritical: Remove unsupported
ignoreFormattingErrors
property.Based on past review analysis, the portal's
getCodeHtml
function doesn't support theignoreFormattingErrors
parameter, making this property unusable.Apply this diff to remove the unsupported property:
code?: { disableCodeHighlight?: boolean; - ignoreFormattingErrors?: boolean; className?: string; };
149-149
:⚠️ Potential issueCritical: Remove unsupported
ignoreFormattingErrors
prop.This prop is being passed to
CodeClient
, but based on past review analysis, the portal'sgetCodeHtml
function doesn't support this parameter.Apply this diff to remove the unsupported prop:
<CodeClient lang={language} {...cleanedProps(props)} code={codeStr.trim()} - ignoreFormattingErrors={code?.ignoreFormattingErrors} className={markdownProps.code?.className} loader={<CodeLoading />} />
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (29)
apps/dashboard/package.json
(1 hunks)apps/portal/package.json
(2 hunks)apps/portal/src/app/Header.tsx
(4 hunks)apps/portal/src/app/chat/page.tsx
(1 hunks)apps/portal/src/app/globals.css
(3 hunks)apps/portal/src/app/references/components/TDoc/PageLayout.tsx
(1 hunks)apps/portal/src/app/references/components/TDoc/Summary.tsx
(1 hunks)apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts
(1 hunks)apps/portal/src/app/references/typescript/[version]/[[...slug]]/page.tsx
(1 hunks)apps/portal/src/components/AI/api.ts
(1 hunks)apps/portal/src/components/AI/chat.tsx
(1 hunks)apps/portal/src/components/Document/Code.tsx
(2 hunks)apps/portal/src/components/code/CodeBlockContainer.tsx
(1 hunks)apps/portal/src/components/code/RenderCode.tsx
(1 hunks)apps/portal/src/components/code/code.client.tsx
(1 hunks)apps/portal/src/components/code/getCodeHtml.tsx
(1 hunks)apps/portal/src/components/code/plaintext-code.tsx
(1 hunks)apps/portal/src/components/markdown/MarkdownRenderer.tsx
(1 hunks)apps/portal/src/components/others/DocSearch.tsx
(2 hunks)apps/portal/src/components/ui/CopyButton.tsx
(1 hunks)apps/portal/src/components/ui/LoadingDots.tsx
(1 hunks)apps/portal/src/components/ui/ScrollShadow/ScrollShadow.module.css
(1 hunks)apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx
(1 hunks)apps/portal/src/components/ui/inline-code.tsx
(1 hunks)apps/portal/src/components/ui/table.tsx
(1 hunks)apps/portal/src/components/ui/textarea.tsx
(1 hunks)apps/portal/src/components/ui/tooltip.tsx
(1 hunks)apps/portal/src/hooks/useClipboard.ts
(1 hunks)apps/portal/src/lib/useIsomorphicLayoutEffect.ts
(1 hunks)
✅ Files skipped from review due to trivial changes (3)
- apps/dashboard/package.json
- apps/portal/src/components/code/plaintext-code.tsx
- apps/portal/src/components/ui/table.tsx
🚧 Files skipped from review as they are similar to previous changes (25)
- apps/portal/src/lib/useIsomorphicLayoutEffect.ts
- apps/portal/src/app/references/typescript/[version]/[[...slug]]/page.tsx
- apps/portal/src/app/references/components/TDoc/Summary.tsx
- apps/portal/src/app/chat/page.tsx
- apps/portal/src/components/others/DocSearch.tsx
- apps/portal/src/components/AI/api.ts
- apps/portal/src/app/references/components/TDoc/utils/getSidebarLinkgroups.ts
- apps/portal/src/components/ui/CopyButton.tsx
- apps/portal/src/components/ui/inline-code.tsx
- apps/portal/src/components/ui/ScrollShadow/ScrollShadow.module.css
- apps/portal/src/components/ui/textarea.tsx
- apps/portal/src/hooks/useClipboard.ts
- apps/portal/src/app/references/components/TDoc/PageLayout.tsx
- apps/portal/src/components/code/getCodeHtml.tsx
- apps/portal/src/components/ui/LoadingDots.tsx
- apps/portal/src/components/ui/ScrollShadow/ScrollShadow.tsx
- apps/portal/src/components/code/code.client.tsx
- apps/portal/src/components/ui/tooltip.tsx
- apps/portal/src/components/code/RenderCode.tsx
- apps/portal/package.json
- apps/portal/src/app/Header.tsx
- apps/portal/src/components/code/CodeBlockContainer.tsx
- apps/portal/src/components/AI/chat.tsx
- apps/portal/src/app/globals.css
- apps/portal/src/components/Document/Code.tsx
🧰 Additional context used
🧬 Code Graph Analysis (1)
apps/portal/src/components/markdown/MarkdownRenderer.tsx (4)
apps/portal/src/components/code/plaintext-code.tsx (1)
PlainTextCodeBlock
(4-29)apps/portal/src/components/code/code.client.tsx (2)
CodeClient
(27-54)CodeLoading
(19-25)apps/portal/src/components/ui/inline-code.tsx (1)
InlineCode
(3-17)apps/portal/src/components/ui/table.tsx (7)
TableContainer
(151-151)Table
(143-143)TableHead
(147-147)TableCell
(149-149)TableHeader
(144-144)TableBody
(145-145)TableRow
(148-148)
⏰ Context from checks skipped due to timeout of 90000ms (8)
- GitHub Check: Unit Tests
- GitHub Check: Size
- GitHub Check: Build Packages
- GitHub Check: E2E Tests (pnpm, esbuild)
- GitHub Check: E2E Tests (pnpm, webpack)
- GitHub Check: E2E Tests (pnpm, vite)
- GitHub Check: Lint Packages
- GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/portal/src/components/markdown/MarkdownRenderer.tsx (6)
1-18
: LGTM: Clean imports and dependencies.The imports are well-organized and correctly reference the portal-specific components and utilities. The type imports for
BundledLanguage
ensure proper TypeScript support.
20-27
: LGTM: Essential helper function for React compatibility.The
cleanedProps
function correctly removes thenode
prop that ReactMarkdown passes to custom components, preventing React warnings about unknown DOM properties.
132-134
: LGTM: onCopy callback properly implemented.The onCopy callback now correctly writes the code to the clipboard, addressing the previous concern about the empty implementation.
116-163
: Well-designed code rendering logic with smart heuristics.The component intelligently differentiates between inline and block code using className and content length heuristics. The fallback to plain text blocks when syntax highlighting is disabled or unavailable is a good UX decision.
173-193
: LGTM: Comprehensive table rendering with portal UI integration.The table rendering properly integrates with the portal's table components and includes proper styling for left-aligned text and muted colors for headers.
194-220
: LGTM: Well-styled list and text rendering.The list components include proper spacing, indentation, and nested list handling. The strong element rendering with medium font weight provides good visual hierarchy.
50096cd
to
2cfa36f
Compare
2cfa36f
to
fac1888
Compare
PR-Codex overview
This PR focuses on enhancing the
portal
application by introducing new features, updating dependencies, and improving existing components for better functionality and styling.Detailed summary
useIsomorphicLayoutEffect
for consistent behavior across SSR and client.react-markdown
from9.0.1
to10.1.0
.id
generation inSummary.tsx
.generateStaticParams
inpage.tsx
.getSidebarLinkgroups.ts
to usehName
.InlineCode
,LoadingDots
,CopyButton
, andScrollShadow
components.Chat
component with AI interaction features.MarkdownRenderer
with better handling of code blocks.Summary by CodeRabbit
New Features
UI Improvements
Bug Fixes
Chores