feat: render links and images in highlight comments

- Parse URLs in comment text and render as clickable links
- Detect image URLs and render inline images
- Add CommentContent component for smart URL rendering
- Style links with primary color and underline
- Style images with border and rounded corners
- Images lazy-load and respect max-width
- Links open in new tab with noopener/noreferrer
This commit is contained in:
Gigi
2025-10-14 11:54:41 +02:00
parent 820daa489e
commit 6ec28e6a9d
2 changed files with 59 additions and 1 deletions

View File

@@ -18,6 +18,58 @@ import { getNostrUrl } from '../config/nostrGateways'
import CompactButton from './CompactButton'
import { HighlightCitation } from './HighlightCitation'
// Helper to detect if a URL is an image
const isImageUrl = (url: string): boolean => {
try {
const urlObj = new URL(url)
const pathname = urlObj.pathname.toLowerCase()
return /\.(jpg|jpeg|png|gif|webp|svg|bmp|ico)(\?.*)?$/.test(pathname)
} catch {
return false
}
}
// Component to render comment with links and inline images
const CommentContent: React.FC<{ text: string }> = ({ text }) => {
// URL regex pattern
const urlPattern = /(https?:\/\/[^\s]+)/g
const parts = text.split(urlPattern)
return (
<>
{parts.map((part, index) => {
if (part.match(urlPattern)) {
if (isImageUrl(part)) {
return (
<img
key={index}
src={part}
alt="Comment attachment"
className="highlight-comment-image"
loading="lazy"
/>
)
} else {
return (
<a
key={index}
href={part}
target="_blank"
rel="noopener noreferrer"
className="highlight-comment-link"
onClick={(e) => e.stopPropagation()}
>
{part}
</a>
)
}
}
return <span key={index}>{part}</span>
})}
</>
)
}
interface HighlightWithLevel extends Highlight {
level?: 'mine' | 'friends' | 'nostrverse'
}
@@ -355,7 +407,9 @@ export const HighlightItem: React.FC<HighlightItemProps> = ({
{highlight.comment && (
<div className="highlight-comment">
<FontAwesomeIcon icon={faComments} flip="horizontal" className="highlight-comment-icon" />
{highlight.comment}
<div className="highlight-comment-text">
<CommentContent text={highlight.comment} />
</div>
</div>
)}

View File

@@ -131,6 +131,10 @@
.highlight-citation { margin-left: 1.25rem; font-size: 0.8rem; color: var(--color-text-secondary); font-style: normal; padding-top: 0.25rem; }
.highlight-comment { margin-top: 0.5rem; padding: 0.75rem; border-radius: 4px; font-size: 0.875rem; color: var(--color-text); line-height: 1.5; display: flex; gap: 0.5rem; align-items: flex-start; word-wrap: break-word; overflow-wrap: break-word; word-break: break-word; min-width: 0; }
.highlight-comment-icon { flex-shrink: 0; margin-top: 0.125rem; }
.highlight-comment-text { flex: 1; min-width: 0; }
.highlight-comment-link { color: var(--color-primary); text-decoration: underline; word-wrap: break-word; overflow-wrap: break-word; }
.highlight-comment-link:hover { opacity: 0.8; }
.highlight-comment-image { display: block; max-width: 100%; height: auto; margin-top: 0.5rem; border-radius: 6px; border: 1px solid var(--color-border); }
/* Level-colored comment icons */
.highlight-item.level-mine .highlight-comment-icon { color: var(--highlight-color-mine, #ffff00); }