'use client'; /** * AutoLinkPreview — the auto-detect link-preview slot, extracted so EVERY * bubble surface shares ONE implementation. * * The default `MessageBubble` and any forked bubble (e.g. cmdop's * mention-aware bubble, which owns its own content `` to * inject `@machine` chips) both render this. Before extraction the logic * lived inline in `MessageBubble` only, so a fork silently lost link * previews — this component is the anti-drift fix: import it, render it, done. * * Behaviour (unchanged from the old inline path): when the host enables * `config.linkPreview.autoDetect` AND provides a `resolve`-r, surface ONE * preview card under the bubble for the FIRST URL in the message text. * Suppressed while streaming (text isn't final), for error turns, and when * the message already carries an explicit `link` block (so we never double * up). Reads everything off the chat context — the caller passes only the * message + presentational flags. */ import { useMemo } from 'react'; import { cn } from '@djangocfg/ui-core/lib'; import { useChatContextOptional } from '../context'; import { extractFirstUrl } from '../core/extractFirstUrl'; import type { ChatMessage } from '../types'; import type { ComposerAppearance } from '../composer/types'; import { LinkPreviewCard } from '../../../common/link-preview'; export interface AutoLinkPreviewProps { message: ChatMessage; /** Streaming turn → suppress (text not final yet). */ isStreaming?: boolean; /** Error turn → suppress. */ isError?: boolean; /** Right-align the card under a user bubble. */ isUser?: boolean; appearance?: ComposerAppearance; } export function AutoLinkPreview({ message, isStreaming = false, isError = false, isUser = false, appearance = 'compact', }: AutoLinkPreviewProps) { const ctx = useChatContextOptional(); const linkCfg = ctx?.config.linkPreview; const url = useMemo(() => { if (!linkCfg?.autoDetect || !linkCfg.resolve) return null; if (isStreaming || isError) return null; if (message.blocks?.some((b) => b.kind === 'link')) return null; return extractFirstUrl(message.content); }, [ linkCfg?.autoDetect, linkCfg?.resolve, isStreaming, isError, message.blocks, message.content, ]); if (!url || !linkCfg?.resolve) return null; return (
); }