import { useState } from "react"; import { cn } from "~/lib/utils"; import { Camera, Download, Copy, Twitter, MessageSquare, Image as ImageIcon, Palette, Settings, } from "lucide-react"; import { Button } from "@/routes/ui/button"; import { AnnotationTools } from "./AnnotationTools"; export interface ChartCaptureMetadata { tokenSymbol: string; tokenName: string; price: number; change24h: number; volume: number; liquidity: number; timestamp: Date; } export interface ChartCaptureSettings { style: "dark" | "light" | "neon"; includeTokenInfo: boolean; includePriceChange: boolean; includeVolumeLiquidity: boolean; includeTimestamp: boolean; includeWatermark: boolean; } export interface ChartCapturePanelProps { /** Current token metadata */ metadata?: ChartCaptureMetadata; /** Callback when capture is clicked */ onCapture?: () => void; /** Callback when export is clicked */ onExport?: (format: "twitter" | "telegram" | "instagram" | "clipboard") => void; /** Additional class names */ className?: string; } /** * ChartCapturePanel - Chart screenshot tool with annotations * * Features: * - One-click chart capture from DexScreener * - Auto-add metadata overlay (token info, price, volume, etc.) * - Drawing tools (lines, arrows, text, shapes, Fibonacci) * - Template styles (dark, light, neon) * - Export options (Twitter, Telegram, Instagram, clipboard) */ export function ChartCapturePanel({ metadata, onCapture, onExport, className, }: ChartCapturePanelProps) { const [settings, setSettings] = useState({ style: "dark", includeTokenInfo: true, includePriceChange: true, includeVolumeLiquidity: true, includeTimestamp: true, includeWatermark: false, }); const [capturedImage, setCapturedImage] = useState(null); const [isCapturing, setIsCapturing] = useState(false); const handleCapture = async () => { setIsCapturing(true); await onCapture?.(); // Mock: simulate capture setTimeout(() => { setCapturedImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="); setIsCapturing(false); }, 1000); }; const handleExport = (format: "twitter" | "telegram" | "instagram" | "clipboard") => { onExport?.(format); }; const formatCurrency = (value: number) => { if (value >= 1000000) return `$${(value / 1000000).toFixed(2)}M`; if (value >= 1000) return `$${(value / 1000).toFixed(1)}K`; return `$${value.toFixed(2)}`; }; return (
{/* Header */}

Chart Capture

{metadata && (

{metadata.tokenSymbol}

)}
{/* Content */}
{/* Capture Button */} {!capturedImage && ( )} {/* Preview */} {capturedImage && (
Captured chart {/* Metadata Overlay Preview */} {metadata && settings.includeTokenInfo && (
{metadata.tokenSymbol}
{settings.includePriceChange && (
= 0 ? "text-green-600" : "text-red-600" )}> ${metadata.price.toFixed(6)} ({metadata.change24h >= 0 ? "+" : ""}{metadata.change24h.toFixed(2)}%)
)}
)}
{/* Annotation Tools */} {/* Recapture Button */}
)} {/* Style Selection */}

Style

{(["dark", "light", "neon"] as const).map((style) => ( ))}
{/* Metadata Options */}

Metadata

{[ { key: "includeTokenInfo" as const, label: "Token info" }, { key: "includePriceChange" as const, label: "Price & change" }, { key: "includeVolumeLiquidity" as const, label: "Volume & liquidity" }, { key: "includeTimestamp" as const, label: "Timestamp" }, { key: "includeWatermark" as const, label: "Watermark" }, ].map(({ key, label }) => ( ))}
{/* Footer - Export Options */} {capturedImage && (

Export

)}
); }