Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

WIP: clickable logic #396

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open

WIP: clickable logic #396

wants to merge 3 commits into from

Conversation

Bran18
Copy link
Contributor

@Bran18 Bran18 commented Feb 26, 2025

Summary by Sourcery

Enhancements:

  • Improve the ClickableText component to make specific parts of the text clickable, allowing users to request more in-depth explanations about those parts.

Summary by CodeRabbit

  • New Features

    • Chat messages now feature enhanced clickable text interactions, offering more consistent interactivity for various text elements such as headings, paragraphs, and lists.
  • Refactor

    • The clickable text component was streamlined to simplify content processing and improve overall structure.
    • The structure of the chat message component has been refined for better readability and maintainability, incorporating a new wrapper for improved rendering logic.

Copy link

vercel bot commented Feb 26, 2025

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

Name Status Preview Comments Updated (UTC)
masterbots ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 26, 2025 10:53pm

Copy link

sourcery-ai bot commented Feb 26, 2025

Reviewer's Guide by Sourcery

This pull request introduces the ImprovedClickableText component, a refactored version of the original ClickableText component, to enhance the clickability and content processing of chat messages. The ChatMessage component is updated to integrate the new ImprovedClickableText component, providing a more robust and user-friendly experience.

Sequence diagram for handling a clickable list item

sequenceDiagram
  participant User
  participant ImprovedClickableText
  participant ChatMessage

  User->>ImprovedClickableText: Clicks on a list item
  activate ImprovedClickableText
  ImprovedClickableText->>ImprovedClickableText: processListItem(element)
  ImprovedClickableText->>ImprovedClickableText: handleClick(clickableText, contextText)
  ImprovedClickableText->>ChatMessage: sendMessageFromResponse(query)
  activate ChatMessage
  ChatMessage-->>ImprovedClickableText: Response
  deactivate ChatMessage
  ImprovedClickableText-->>User: Updates UI
  deactivate ImprovedClickableText
Loading

File-Level Changes

Change Details Files
Refactored ClickableText component to ImprovedClickableText for enhanced clickability and content processing.
  • Replaced the original ClickableText component with ImprovedClickableText.
  • Implemented more robust logic for identifying and processing clickable elements within the text.
  • Added support for headings and list items with specific patterns (e.g., 'M1:', numbered headings).
  • Improved handling of nested elements and text extraction.
  • Introduced hover states for visual feedback on clickable elements.
  • Simplified the component's props to accept only children and sendMessageFromResponse.
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx
apps/masterbots.ai/components/routes/chat/chat-message.tsx
Modified ChatMessage component to integrate the new ImprovedClickableText component.
  • Replaced the import of ClickableText with ImprovedClickableText.
  • Wrapped the message content with the ImprovedClickableText component when the message is not from the user and sendMessageFromResponse is provided.
  • Adjusted the ChatMessage component to use the new EnhancedContent wrapper for clickable text functionality.
  • Updated the Markdown component's p, h1, h2, h3, strong, and li renderers to use the EnhancedContent wrapper.
apps/masterbots.ai/components/routes/chat/chat-message.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

coderabbitai bot commented Feb 26, 2025

Walkthrough

This pull request refactors the ClickableText component by removing the extractTextFromReactNodeWeb function and simplifying the extraction logic to utilize only extractTextFromReactNodeNormal. A new function, processContent, is introduced for recursive content processing. Additionally, the ChatMessage component is enhanced with an EnhancedContent wrapper for improved rendering of clickable elements based on message context. The overall structure and readability of both components are improved by consolidating functionality and removing redundant code.

Changes

File Change Summary
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx Refactored ClickableText component; removed extractTextFromReactNodeWeb; introduced processContent for recursive processing; eliminated processNestedContent; updated rendering logic to return processed content directly.
apps/masterbots.ai/components/routes/chat/chat-message.tsx Added EnhancedContent wrapper for conditional rendering based on message sender's role; updated rendering logic for Markdown elements to utilize EnhancedContent; simplified mapping of references; refined overall structure with comments and removed commented-out code.

Possibly related PRs

  • Fix: clickable text feature #372: The changes in the main PR are related to the modifications in the ClickableText component and its rendering logic, which also focus on enhancing clickable text features.
  • feat: improve clickable text with sentence content #388: The changes in the main PR align with modifications in the ClickableText component regarding content handling and rendering logic.
  • impr: web search response #310: The main PR's refactoring of the ClickableText component is related to enhancements in the ChatMessage component's handling of clickable text through the EnhancedContent wrapper.

Suggested labels

enhancement

Suggested reviewers

  • AndlerRL

Poem

I hop through lines of clever code,
Transforming text on every node.
The old gives way to something new,
With clicks that shine like morning dew.
A bunny celebrates this bright update,
Tech magic in each fresh, nimble crate!

Warning

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

🔧 ESLint

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

apps/masterbots.ai/components/routes/chat/chat-message.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.

The config "next/core-web-vitals" was referenced from the config file in "/apps/masterbots.ai/.eslintrc.json".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx

Oops! Something went wrong! :(

ESLint: 8.57.1

ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.

The config "next/core-web-vitals" was referenced from the config file in "/apps/masterbots.ai/.eslintrc.json".

If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.

✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @Bran18 - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a visual distinction for clickable elements to improve discoverability.
  • The component is becoming quite complex; consider refactoring to smaller, more focused functions to improve readability and maintainability.
Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

return processNestedContent(content)
// Process list items with bold/strong text
const processListItem = (element: React.ReactElement) => {
console.log('Processing list item:', element.type)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Remove or conditionally disable console.log debugging statements.

Consider removing or wrapping the console.log calls (e.g., in processListItem and processHeading) behind a development flag to avoid unintended logging in production.

Suggested implementation:

  const processListItem = (element: React.ReactElement) => {
    if (process.env.NODE_ENV === 'development') {
      console.log('Processing list item:', element.type)
    }
    const fullItemText = extractTextContent(element.props.children)
    if (process.env.NODE_ENV === 'development') {
      console.log('Full item text:', fullItemText)
    }

const processTextNode = (text: string): ReactNode => {
// Check for M1/M2 patterns
const match = text.match(/^(M\d+):\s*(.+)/)
if (!match) return text
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (!match) return text
if (!match) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 037e41e and 2fe115d.

📒 Files selected for processing (2)
  • apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (1 hunks)
  • apps/masterbots.ai/components/routes/chat/chat-message.tsx (5 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx

[error] 142-142: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 318-318: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 334-334: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (17)
apps/masterbots.ai/components/routes/chat/chat-message.tsx (8)

10-10: Use of ImprovedClickableText import is consistent.
This import aligns well with the new component’s naming, improving clarity and consistency.


12-14: Good documentation block.
Thank you for adding a descriptive doc comment. This improves maintainability by clarifying the component’s purpose.


23-36: Minor additions look fine.
No issues found with the added comments and reference handling.


67-78: Neat conditional wrapper in EnhancedContent.
The logic to only wrap children in ImprovedClickableText when the user role and sendMessageFromResponse are appropriate keeps the component flexible and avoids unneeded overhead. This is a clean approach.


87-127: Markdown elements’ integration is nicely structured.
Wrapping each block (paragraph, headings, strong text) within EnhancedContent is a straightforward way to handle clickable text. The usage of the new wrapper ensures a consistent experience across different Markdown nodes. The approach is readable and clear.


129-136: Smart handling of nested list items.
The logic checks for nested lists and applies the EnhancedContent wrapper accordingly, preventing unexpected styling or clickable behavior from cascading incorrectly.

Also applies to: 143-145


199-202: ChatMessageActions gating is clear.
Conditionally rendering actions based on actionRequired is straightforward and keeps the UI uncluttered when actions are unnecessary.


207-207: Overall structure is clean.
The closing bracket finalizes a well-structured component. No issues found.

apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (9)

1-1: Import statements look good.
No concerns with importing React, state hooks, and utility functions.


4-7: Interface simplification is beneficial.
Condensing the props to only children and sendMessageFromResponse helps maintain clarity and reduces complexity.


9-11: Concise documentation.
The doc comment succinctly states the component’s goal.


12-17: Initialization of hover state.
Tracking hoveredElement ensures each element can be distinctly styled. This approach is straightforward and easy to maintain.


66-81: handleClick gracefully warns if callback is missing.
Emitting a console warning avoids silent failures. Good practice to inform developers about missing props.


82-186: processListItem logic is comprehensive.
It effectively checks for strong elements, recognized label patterns, and splits context text. The approach covers multiple clickable scenarios in list items without overly complicating the code.

🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


188-245: processHeading showcases a user-friendly clickable heading design.
Extracting numeric or labeled headings and making them clickable fosters a consistent interactive experience. The detail included (underlines on hover, splitting parts, etc.) is well-executed.


247-275: processTextNode handles M# patterns well.
Recognizing and extracting labeled segments like M1 followed by text is a neat feature for dynamic interactions.


277-345: processContent elegantly handles nested structures.
Traversing strings, arrays, and React elements in a unified manner results in cleaner, centralized logic for clickable text transformations.

🧰 Tools
🪛 Biome (1.9.4)

[error] 318-318: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)


[error] 334-334: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

(child: ReactNode) => {
// If we have a strong element and this child is a strong element
if (hasStrong && React.isValidElement(child) && (child.type === 'strong' || child.type === 'b')) {
return React.cloneElement(child as React.ReactElement<React.PropsWithChildren<{}>>, {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace {} with a more specific or generic type.
Using '{}' as a type can be misleading. Prefer a more explicit type (e.g., unknown, Record<string, unknown>) to align with TypeScript best practices and static analysis guidance.

Below is a sample fix for each occurrence:

- React.ReactElement<React.PropsWithChildren<{}>>
+ React.ReactElement<React.PropsWithChildren<unknown>>

Also applies to: 318-318, 334-334

🧰 Tools
🪛 Biome (1.9.4)

[error] 142-142: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (5)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (5)

85-85: 🛠️ Refactor suggestion

Remove console.log statements.

There are multiple console.log statements throughout the code that should be removed or wrapped in a development-only check.

-console.log('Processing list item:', element.type)
+if (process.env.NODE_ENV === 'development') {
+  console.log('Processing list item:', element.type)
+}

89-89: 🛠️ Refactor suggestion

Remove or conditionally disable console.log debugging.

-console.log('Full item text:', fullItemText)
+if (process.env.NODE_ENV === 'development') {
+  console.log('Full item text:', fullItemText)
+}

130-131: 🛠️ Refactor suggestion

Remove or conditionally disable console.log debugging.

-console.log('Clickable text found:', clickableText)
-console.log('Context text:', contextText)
+if (process.env.NODE_ENV === 'development') {
+  console.log('Clickable text found:', clickableText)
+  console.log('Context text:', contextText)
+}

197-197: 🛠️ Refactor suggestion

Remove or conditionally disable console.log debugging.

-console.log('Processing heading:', headingText)
+if (process.env.NODE_ENV === 'development') {
+  console.log('Processing heading:', headingText)
+}

207-207: 🛠️ Refactor suggestion

Remove or conditionally disable console.log debugging.

-console.log('Found heading title:', title)
+if (process.env.NODE_ENV === 'development') {
+  console.log('Found heading title:', title)
+}
🧹 Nitpick comments (5)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (5)

293-295: Avoid using array index as React key.

Using array index as a key in React lists can lead to performance issues and bugs when the list changes.

Consider generating a more stable key:

-<React.Fragment key={`content-item-${// biome-ignore lint/suspicious/noArrayIndexKey: <explanation>
-index}`}>
+<React.Fragment key={`content-item-${typeof item === 'string' ? item.slice(0, 10) : index}-${index}`}>

75-78: Use optional chaining instead of ignoring the lint rule.

-// biome-ignore lint/complexity/useOptionalChain: <explanation>
-if (contextText && contextText.trim()) {
+if (contextText?.trim()) {

116-124: Use optional chaining instead of ignoring the lint rule.

-// biome-ignore lint/complexity/useOptionalChain: <explanation>
-if (match && match[1]) {
+if (match?.[1]) {

334-339: Use optional chaining instead of ignoring the lint rule.

-// biome-ignore lint/complexity/useOptionalChain: <explanation>
-if (content.props && content.props.children) {
+if (content.props?.children) {

13-346: Consider extracting reusable click handler components.

The component has multiple places where it renders clickable spans with very similar logic. This creates duplication and makes maintenance harder.

Consider creating a reusable ClickableSpan component:

type ClickableSpanProps = {
  text: string;
  contextText?: string;
  onClick: (text: string, contextText?: string) => void;
  className?: string;
  elementId: string;
  hoveredElement: string | null;
  setHoveredElement: (id: string | null) => void;
};

const ClickableSpan = ({
  text,
  contextText,
  onClick,
  className,
  elementId,
  hoveredElement,
  setHoveredElement
}: ClickableSpanProps) => {
  const isHovered = hoveredElement === elementId;
  
  return (
    <span 
      className={cn(
        'cursor-pointer transition-colors duration-200 text-blue-500',
        isHovered ? 'underline' : 'hover:underline',
        className
      )}
      onClick={() => onClick(text, contextText)}
      onMouseEnter={() => setHoveredElement(elementId)}
      onMouseLeave={() => setHoveredElement(null)}
      tabIndex={0}
      role="button"
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onClick(text, contextText);
        }
      }}
      aria-label={`Get more details about ${text}`}
    >
      {text}
    </span>
  );
};

Then, use it in your processing functions instead of duplicating the span logic.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe115d and df46f99.

📒 Files selected for processing (1)
  • apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (1 hunks)
🔇 Additional comments (3)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (3)

27-28: Add block braces to if statements to improve code safety.

The conditional statements for extractTextContent are missing block braces, which could lead to bugs when statements are added later.

-if (typeof node === 'string') return node
-if (typeof node === 'number') return node.toString()
+if (typeof node === 'string') {
+  return node
+}
+if (typeof node === 'number') {
+  return node.toString()
+}

143-143: Replace {} with a more specific or generic type.

Using '{}' as a type can be misleading. Use a more explicit type such as unknown or Record<string, unknown>.

-return React.cloneElement(child as React.ReactElement<React.PropsWithChildren<{}>>, {
+return React.cloneElement(child as React.ReactElement<React.PropsWithChildren<unknown>>, {

252-252: Add block braces to if statements.

-if (!match) return text
+if (!match) {
+  return text
+}

Comment on lines 222 to 236
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(title)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{parts.slice(1).join('.')}
</span>
</>
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add keyboard accessibility to clickable spans.

Same accessibility issue in the processHeading function.

-{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span 
  className={cn(
    'cursor-pointer transition-colors duration-200 text-blue-500',
    isHovered ? 'underline' : 'hover:underline'
  )}
  onClick={() => handleClick(title)}
  onMouseEnter={() => setHoveredElement(elementId)}
  onMouseLeave={() => setHoveredElement(null)}
+  tabIndex={0}
+  role="button"
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      handleClick(title);
+    }
+  }}
>
📝 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.

Suggested change
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(title)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{parts.slice(1).join('.')}
</span>
</>
)
}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(title)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
tabIndex={0}
role="button"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick(title);
}
}}
>
{parts.slice(1).join('.')}
</span>
</>
)
}

Comment on lines 162 to 178
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer hover:underline',
isListItem ? 'text-blue-500' : 'text-link'
)}
onClick={createClickHandler(
clickableText,
fullContext || strongContent
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
type="button"
tabIndex={0}
onClick={() => handleClick(clickableText, contextText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{strongContent}
</button>
)
}
return content
{parts[0]}
</span>
<span>:{parts.slice(1).join(':')}</span>
</>
)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add keyboard accessibility to clickable spans.

The clickable spans are missing keyboard accessibility. Users should be able to trigger the click event using keyboard.

-{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span 
  className={cn(
    'cursor-pointer transition-colors duration-200 text-blue-500',
    isHovered ? 'underline' : 'hover:underline'
  )}
  onClick={() => handleClick(clickableText, contextText)}
  onMouseEnter={() => setHoveredElement(elementId)}
  onMouseLeave={() => setHoveredElement(null)}
+  tabIndex={0}
+  role="button"
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      handleClick(clickableText, contextText);
+    }
+  }}
>
📝 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.

Suggested change
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer hover:underline',
isListItem ? 'text-blue-500' : 'text-link'
)}
onClick={createClickHandler(
clickableText,
fullContext || strongContent
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
type="button"
tabIndex={0}
onClick={() => handleClick(clickableText, contextText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{strongContent}
</button>
)
}
return content
{parts[0]}
</span>
<span>:{parts.slice(1).join(':')}</span>
</>
)
}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(clickableText, contextText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
tabIndex={0}
role="button"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick(clickableText, contextText);
}
}}
>
{parts[0]}
</span>
<span>:{parts.slice(1).join(':')}</span>
</>
)
}

Comment on lines 261 to 275
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(label, restText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{label}
</span>
<span>: {restText}</span>
</>
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add keyboard accessibility to clickable spans.

Same accessibility issue in the processTextNode function.

-{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span 
  className={cn(
    'cursor-pointer transition-colors duration-200 text-blue-500',
    isHovered ? 'underline' : 'hover:underline'
  )}
  onClick={() => handleClick(label, restText)}
  onMouseEnter={() => setHoveredElement(elementId)}
  onMouseLeave={() => setHoveredElement(null)}
+  tabIndex={0}
+  role="button"
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') {
+      e.preventDefault();
+      handleClick(label, restText);
+    }
+  }}
>
📝 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.

Suggested change
{/* biome-ignore lint/a11y/useKeyWithClickEvents: <explanation> */}
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(label, restText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
>
{label}
</span>
<span>: {restText}</span>
</>
)
<span
className={cn(
'cursor-pointer transition-colors duration-200 text-blue-500',
isHovered ? 'underline' : 'hover:underline'
)}
onClick={() => handleClick(label, restText)}
onMouseEnter={() => setHoveredElement(elementId)}
onMouseLeave={() => setHoveredElement(null)}
tabIndex={0}
role="button"
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick(label, restText);
}
}}
>
{label}
</span>
<span>: {restText}</span>
</>
)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (1)

110-116: Avoid using array indices as keys if the rendered list can change.
Using indices is acceptable for static or ephemeral lists, but can cause reconciliation issues otherwise. Consider a more stable key if the content can reorder or update.

- <React.Fragment key={index}>
+ <React.Fragment key={`node-${index}-${Math.random()}`}>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between df46f99 and 0d5f812.

📒 Files selected for processing (2)
  • apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (3 hunks)
  • apps/masterbots.ai/components/routes/chat/chat-message.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/masterbots.ai/components/routes/chat/chat-message.tsx
🔇 Additional comments (10)
apps/masterbots.ai/components/routes/chat/chat-clickable-text.tsx (10)

4-4: No issues with the updated import.
The introduction of parseClickableText seems appropriate and consistent with the rest of the logic.


21-21: Consider sanitizing or validating the text argument.
If this handler is exposed to user input, ensure malicious or unintended strings aren't passed on to other functions or APIs without validation or sanitization.


32-37: Good documentation.
The docstring clearly explains the recursive content-processing workflow, aiding maintainability.


39-46: Verify behavior when multiple colons appear.
The current logic extracts clickable text before the first colon. If multiple colons exist, only the first is treated as clickable. Confirm this aligns with the intended design.


47-61: Solid use of a element for accessibility.
Using a native <button> element automatically provides keyboard interaction and is recommended for clickable text.


63-65: Straightforward validity check.
No concerns; leveraging React.isValidElement is clear and helps avoid runtime errors.


66-95: Appending a missing colon may lead to unintended clickable segments.
Appending the colon ensures the parser can handle strong/b text consistently. However, verify that this is always intended, especially if any text should remain unclickable when missing a colon.


97-105: Recursive processing of element children is correct.
Cloning the element with processed children prevents dropping nested structures. No issues found.


118-120: Return content as-is when it’s neither string nor element.
This fallback case is concise and sensible.


122-122: Final return looks good.
Rendering the processed content in a React fragment is neat and non-obtrusive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant