import { NodeViewWrapper } from '@tiptap/react' import React, { useState, useRef, useEffect, useMemo } from 'react' import { Upload, Link as LinkIcon, GripVertical, GripHorizontal, AlignCenter, Cuboid, Code } from 'lucide-react' import { useEditorProvider } from '@components/Contexts/Editor/EditorContext' import { SiGithub, SiReplit, SiSpotify, SiLoom, SiGooglemaps, SiCodepen, SiCanva, SiNotion, SiGoogledocs, SiGitlab, SiX, SiFigma, SiGiphy, SiYoutube } from '@icons-pack/react-simple-icons' import { useRouter } from 'next/navigation' import DOMPurify from 'dompurify' // Add new type for script-based embeds const SCRIPT_BASED_EMBEDS = { twitter: { src: 'https://platform.twitter.com/widgets.js', identifier: 'twitter-tweet' }, instagram: { src: 'https://www.instagram.com/embed.js', identifier: 'instagram-media' }, tiktok: { src: 'https://www.tiktok.com/embed.js', identifier: 'tiktok-embed' }, // Add more platforms as needed }; // Helper function to convert YouTube URLs to embed format const getYouTubeEmbedUrl = (url: string): string => { try { // First validate that this is a proper URL const parsedUrl = new URL(url); // Ensure the hostname is actually YouTube const isYoutubeHostname = parsedUrl.hostname === 'youtube.com' || parsedUrl.hostname === 'www.youtube.com' || parsedUrl.hostname === 'youtu.be' || parsedUrl.hostname === 'www.youtu.be'; if (!isYoutubeHostname) { return url; // Not a YouTube URL, return as is } // Handle different YouTube URL formats with a more precise regex const youtubeRegex = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i; const match = url.match(youtubeRegex); if (match && match[1]) { // Validate the video ID format (should be exactly 11 characters) const videoId = match[1]; if (videoId.length === 11) { // Return the embed URL with the video ID and secure protocol return `https://www.youtube.com/embed/${videoId}?autoplay=0&rel=0`; } } // If no valid match found, return the original URL return url; } catch (e) { // If URL parsing fails, return the original URL return url; } }; // Add new memoized component for the embed content const MemoizedEmbed = React.memo(({ embedUrl, sanitizedEmbedCode, embedType }: { embedUrl: string; sanitizedEmbedCode: string; embedType: 'url' | 'code'; }) => { useEffect(() => { if (embedType === 'code' && sanitizedEmbedCode) { // Check for any matching script-based embeds const matchingPlatform = Object.entries(SCRIPT_BASED_EMBEDS).find(([_, config]) => sanitizedEmbedCode.includes(config.identifier) ); if (matchingPlatform) { const [_, config] = matchingPlatform; const script = document.createElement('script'); script.src = config.src; script.async = true; script.charset = 'utf-8'; document.body.appendChild(script); return () => { document.body.removeChild(script); }; } } }, [embedType, sanitizedEmbedCode]); if (embedType === 'url' && embedUrl) { // Process the URL if it's a YouTube URL - using proper URL validation let isYoutubeUrl = false; try { const url = new URL(embedUrl); // Check if the hostname is exactly youtube.com or youtu.be (or www variants) isYoutubeUrl = url.hostname === 'youtube.com' || url.hostname === 'www.youtube.com' || url.hostname === 'youtu.be' || url.hostname === 'www.youtu.be'; } catch (e) { // Invalid URL format, not a YouTube URL isYoutubeUrl = false; } const processedUrl = isYoutubeUrl ? getYouTubeEmbedUrl(embedUrl) : embedUrl; return (