"use client"; import React, { useState, useEffect, useRef, memo } from "react"; import { MessageSquare, Send, Code2, Palette, Search, Trash2, Copy, Monitor, StopCircle, X, Zap, Ghost, Wand2, LayoutPanelLeft, Play, Orbit } from "lucide-react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import rehypeHighlight from "rehype-highlight"; import { cn } from "@/lib/utils"; import { AIAssistMessage } from "@/types"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import useStore from "@/lib/store"; import { translations } from "@/lib/i18n/translations"; import modelAdapter from "@/lib/services/adapter-instance"; // --- Types --- interface PreviewData { type: string; data: string; language?: string; isStreaming?: boolean; } // --- Specialized Components --- /** * A ultra-stable iframe wrapper that avoids hydration issues * and provides a WOW visual experience. */ const LiveCanvas = memo(({ data, type, isStreaming }: { data: string, type: string, isStreaming: boolean }) => { const iframeRef = useRef(null); useEffect(() => { if (!iframeRef.current || !data) return; // Decode HTML entities if present const isEncodedHtml = data.includes("<") && data.includes(">"); const normalized = isEncodedHtml ? data .replace(/</g, "<") .replace(/>/g, ">") .replace(/&/g, "&") .replace(/"/g, "\"") .replace(/'/g, "'") : data; // Check if the content is a full HTML document or a fragment const trimmed = normalized.trim(); const isFullDocument = /^]/i.test(normalized); let doc: string; if (isFullDocument) { // If it's a full document, inject Tailwind CSS but keep the structure if (hasHeadTag) { doc = normalized.replace(//i, ` `); } else { doc = normalized.replace(/]*>/i, (match) => `${match} `); } } else { // Wrap fragments in a styled container doc = ` ${normalized} `; } iframeRef.current.srcdoc = doc; }, [data, type]); return (