| 'use client'; |
|
|
| import React, { useState, useMemo, useEffect } from 'react'; |
| import { Button } from '@/components/ui/button'; |
| import { ScrollArea } from '@/components/ui/scroll-area'; |
| import { Code, Monitor, ExternalLink } from 'lucide-react'; |
| import { cn } from '@/lib/utils'; |
| import { constructHtmlPreviewUrl } from '@/lib/utils/url'; |
| import type { Project } from '@/lib/api'; |
|
|
| interface HtmlRendererProps { |
| content: string; |
| previewUrl: string; |
| className?: string; |
| project?: Project; |
| } |
|
|
| |
| |
| |
| export function HtmlRenderer({ |
| content, |
| previewUrl, |
| className, |
| project |
| }: HtmlRendererProps) { |
| const [viewMode, setViewMode] = useState<'preview' | 'code'>('preview'); |
|
|
| |
| const blobHtmlUrl = useMemo(() => { |
| if (content && !project?.sandbox?.sandbox_url) { |
| const blob = new Blob([content], { type: 'text/html' }); |
| return URL.createObjectURL(blob); |
| } |
| return undefined; |
| }, [content, project?.sandbox?.sandbox_url]); |
|
|
| |
| const fileName = useMemo(() => { |
| try { |
| |
| if (previewUrl.includes('/api/sandboxes/')) { |
| const url = new URL(previewUrl); |
| const path = url.searchParams.get('path'); |
| if (path) { |
| return path.split('/').pop() || ''; |
| } |
| } |
|
|
| |
| return previewUrl.split('/').pop() || ''; |
| } catch (e) { |
| console.error('Error extracting filename:', e); |
| return ''; |
| } |
| }, [previewUrl]); |
|
|
| |
| const htmlPreviewUrl = useMemo(() => { |
| if (project?.sandbox?.sandbox_url && fileName) { |
| return constructHtmlPreviewUrl(project.sandbox.sandbox_url, fileName); |
| } |
| return blobHtmlUrl || previewUrl; |
| }, [project?.sandbox?.sandbox_url, fileName, blobHtmlUrl, previewUrl]); |
|
|
| |
| useEffect(() => { |
| return () => { |
| if (blobHtmlUrl) { |
| URL.revokeObjectURL(blobHtmlUrl); |
| } |
| }; |
| }, [blobHtmlUrl]); |
|
|
| return ( |
| <div className={cn('w-full h-full flex flex-col', className)}> |
| {/* Content area */} |
| <div className="flex-1 min-h-0 relative"> |
| {viewMode === 'preview' ? ( |
| <div className="w-full h-full"> |
| <iframe |
| src={htmlPreviewUrl} |
| title="HTML Preview" |
| className="w-full h-full border-0" |
| sandbox="allow-same-origin allow-scripts" |
| style={{ background: 'white' }} |
| /> |
| </div> |
| ) : ( |
| <ScrollArea className="w-full h-full"> |
| <pre className="p-4 overflow-auto"> |
| <code className="text-sm">{content}</code> |
| </pre> |
| </ScrollArea> |
| )} |
| </div> |
| </div> |
| ); |
| } |