'use client'; import React, { useEffect, useState } from 'react'; import { Globe } from 'lucide-react'; // Satélite: a ext API não expõe /link-preview — stub que rejeita (o componente // cai para "sem preview" no catch, sem quebrar a renderização). const api = { get: (_url: string): Promise => Promise.reject(new Error('no link-preview')) }; interface PreviewData { title: string; description: string; image: string; url: string; domain: string; } // Simple in-memory cache so the same URL is only fetched once per page load const cache = new Map(); interface LinkPreviewProps { url: string; isOut: boolean; } const LinkPreview: React.FC = ({ url, isOut }) => { const [data, setData] = useState(cache.has(url) ? cache.get(url)! : undefined as any); const [loading, setLoading] = useState(!cache.has(url)); useEffect(() => { if (cache.has(url)) { setData(cache.get(url) ?? null); setLoading(false); return; } let cancelled = false; setLoading(true); api.get(`/link-preview?url=${encodeURIComponent(url)}`) .then((d: PreviewData) => { if (cancelled) return; const value = d?.title ? d : null; cache.set(url, value); setData(value); }) .catch(() => { if (!cancelled) { cache.set(url, null); setData(null); } }) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, [url]); if (loading) { return (
); } if (!data) return null; return ( e.stopPropagation()} className={`mt-2 block rounded-xl overflow-hidden border transition-all hover:shadow-md active:scale-[0.98] no-underline ${isOut ? 'border-green-200 bg-white/70 hover:bg-white/90' : 'border-gray-200 bg-gray-50 hover:bg-white' }`} > {data.image && ( {data.title} { (e.currentTarget as HTMLImageElement).style.display = 'none'; }} /> )}
{data.domain}
{data.title && (

{data.title}

)} {data.description && (

{data.description}

)}
); }; // URL extraction helper (exported for use in MessageBubble) const URL_RE = /https?:\/\/[^\s<>"{}|\\^`[\]]{4,}/; export const extractFirstUrl = (text: string | null | undefined): string | null => { if (!text) return null; const m = text.match(URL_RE); return m ? m[0] : null; }; // Renders plain text with URLs converted to clickable tags export const renderTextWithLinks = (text: string): React.ReactNode[] => { const parts = text.split(/(https?:\/\/[^\s<>"{}|\\^`[\]]{4,})/g); return parts.map((part, i) => { if (/^https?:\/\//.test(part)) { return ( e.stopPropagation()} className="text-blue-600 underline break-all hover:text-blue-700" > {part} ); } return {part}; }); }; export default LinkPreview;