-
Notifications
You must be signed in to change notification settings - Fork 1
impr: web search response #310
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
Changes from all commits
dece88f
2170549
c50f3ae
1bcb051
9bb3e06
b3ea1c2
9203c7f
f1972b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,51 +1,137 @@ | ||
| import { | ||
| cleanClickableText, | ||
| extractTextFromReactNode, | ||
| parseClickableText | ||
| } from '@/lib/utils' | ||
|
|
||
| interface ClickableTextProps { | ||
| children: React.ReactNode | ||
| isListItem: boolean | ||
| sendMessageFromResponse?: (message: string) => void | ||
| } | ||
| extractTextFromReactNodeNormal, | ||
| extractTextFromReactNodeWeb, | ||
| parseClickableText, | ||
| transformLink, | ||
| } from '@/lib/clickable-results' | ||
| import { useThread } from '@/lib/hooks/use-thread' | ||
| import { cn } from '@/lib/utils' | ||
| import type { ClickableTextProps } from '@/types/types' | ||
| import React from 'react' | ||
|
|
||
| /** | ||
| * ClickableText component | ||
| * Renders phrases as clickable links, triggering a message when clicked. | ||
| */ | ||
| export function ClickableText({ | ||
| children, | ||
| isListItem, | ||
| sendMessageFromResponse | ||
| sendMessageFromResponse, | ||
| webSearchResults = [], | ||
| onReferenceFound, | ||
| }: ClickableTextProps) { | ||
| const fullText = extractTextFromReactNode(children) | ||
| const { clickableText, restText } = parseClickableText(fullText) | ||
| const { webSearch } = useThread() | ||
|
|
||
| const extractedContent = webSearch | ||
| ? extractTextFromReactNodeWeb(children) | ||
| : extractTextFromReactNodeNormal(children) | ||
|
|
||
| const handleClick = () => { | ||
| if (sendMessageFromResponse && clickableText.trim()) { | ||
| const cleanedText = cleanClickableText(clickableText) | ||
| const createClickHandler = (text: string) => () => { | ||
| if (sendMessageFromResponse && text.trim()) { | ||
| const cleanedText = cleanClickableText(text) | ||
| sendMessageFromResponse(`Tell me more about ${cleanedText}`) | ||
| } | ||
| } | ||
|
|
||
Bran18 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (!clickableText.trim()) { | ||
| return <>{fullText}</> | ||
| const processLink = (linkElement: React.ReactElement) => { | ||
| const href = linkElement.props.href | ||
| // Buscar la referencia correspondiente | ||
| const reference = webSearchResults.find((result) => result.url === href) | ||
|
|
||
| if (reference && onReferenceFound) { | ||
| onReferenceFound(reference) | ||
| return null // Remover el link inline | ||
| } | ||
|
|
||
| return linkElement // Mantener links que no son de búsqueda web | ||
| } | ||
|
|
||
| return ( | ||
| const renderClickableContent = (clickableText: string, restText: string) => ( | ||
| <> | ||
| {/* biome-ignore lint/a11y/useKeyWithClickEvents: Click handler is supplementary */} | ||
| <span | ||
| className="cursor-pointer text-link hover:underline" | ||
| onClick={handleClick} | ||
| // biome-ignore lint/a11y/useSemanticElements: <explanation> | ||
| role="button" | ||
| tabIndex={0} | ||
| <button | ||
| className={cn('cursor-pointer hover:underline bg-transparent border-none p-0 m-0', isListItem ? 'text-blue-500' : 'text-link')} | ||
| onClick={createClickHandler(clickableText)} | ||
| type="button" | ||
| > | ||
| {clickableText} | ||
| </span> | ||
| </button> | ||
| {restText} | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| if (Array.isArray(extractedContent)) { | ||
| return extractedContent.map((content, index) => { | ||
| if (React.isValidElement(content)) { | ||
| if (content.type === 'a' && webSearch) { | ||
| return processLink(content) | ||
| } | ||
| // Manejo de elementos strong | ||
| if (content.type === 'strong') { | ||
| const strongContent = extractTextFromReactNodeNormal( | ||
| (content.props as { children: React.ReactNode }).children, | ||
| ) | ||
| const { clickableText, restText } = parseClickableText(strongContent + ':') | ||
|
|
||
| if (clickableText.trim()) { | ||
| return ( | ||
| <button | ||
| key={`clickable-${index}`} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace array indices with stable unique keys. Using array indices as React keys can lead to rendering issues when items are reordered or removed. -key={`clickable-${index}`}
+key={`clickable-${clickableText}-${index}`}Also applies to: 119-119 🧰 Tools🪛 Biome (1.9.4)[error] 75-75: Avoid using the index of an array as key property in an element. This is the source of the key value. The order of the items may change, and this also affects performances and component state. (lint/suspicious/noArrayIndexKey) |
||
| className={cn( | ||
| 'cursor-pointer hover:underline', | ||
| isListItem ? 'text-blue-500' : 'text-link', | ||
| )} | ||
| onClick={createClickHandler(clickableText)} | ||
| type="button" | ||
| tabIndex={0} | ||
| > | ||
| {strongContent} | ||
| </button> | ||
| ) | ||
| } | ||
| return content | ||
| } | ||
|
|
||
| // Manejo de links cuando webSearch está activo | ||
| if (content.type === 'a' && webSearch) { | ||
| const parentContext = extractedContent | ||
| .filter( | ||
| (item) => | ||
| typeof item === 'string' || (React.isValidElement(item) && item.type === 'strong'), | ||
| ) | ||
| .map((item) => | ||
| typeof item === 'string' | ||
| ? item | ||
| : extractTextFromReactNodeNormal( | ||
| (item.props as { children: React.ReactNode }).children, | ||
| ), | ||
| ) | ||
| .join(' ') | ||
| return transformLink(content, parentContext) | ||
| } | ||
|
|
||
| return content | ||
| } | ||
|
|
||
| const { clickableText, restText } = parseClickableText(String(content)) | ||
|
|
||
| if (!clickableText.trim()) { | ||
| return content | ||
| } | ||
|
|
||
| return ( | ||
| <React.Fragment key={`clickable-${index}`}> | ||
| {renderClickableContent(clickableText, restText)} | ||
| </React.Fragment> | ||
| ) | ||
| }) | ||
| } | ||
|
|
||
| if (React.isValidElement(extractedContent)) { | ||
| return extractedContent | ||
| } | ||
|
|
||
| const { clickableText, restText } = parseClickableText(String(extractedContent)) | ||
|
|
||
| if (!clickableText.trim()) { | ||
| return <>{extractedContent}</> | ||
| } | ||
|
|
||
| return renderClickableContent(clickableText, restText) | ||
| } | ||
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.
suggestion (bug_risk): Consider throwing or logging the original error for better debugging
While returning a user-friendly message is good, completely swallowing the original error makes debugging harder. Consider logging the full error details or maintaining the throw with a wrapped error that includes both messages.