import React, { useState, useRef } from 'react'; import { Wand2, MousePointerClick, Heading1, AlignLeft, Copy, CheckCircle2, Sparkles, AlertCircle, Lightbulb, Smile, Code, LayoutTemplate, RotateCcw } from 'lucide-react'; const apiKey = ""; // API key is provided by the execution environment // --- Utility Functions --- const copyToClipboard = (text) => { return new Promise((resolve, reject) => { // Create an invisible textarea to execute the copy command on const textArea = document.createElement("textarea"); textArea.value = text; textArea.style.position = "fixed"; textArea.style.left = "-999999px"; document.body.appendChild(textArea); textArea.focus(); textArea.select(); const handleCopy = (e) => { // Encode emojis as HTML entities for rich text editors const htmlText = Array.from(text) .map(char => char.codePointAt(0) > 127 ? `&#${char.codePointAt(0)};` : char) .join(''); e.clipboardData.setData('text/plain', text); e.clipboardData.setData('text/html', `${htmlText}`); e.preventDefault(); }; // Attach listener specifically for this operation document.addEventListener('copy', handleCopy); try { const successful = document.execCommand('copy'); if (successful) { resolve(); } else { reject(new Error('Copy command failed')); } } catch (err) { reject(err); } finally { document.removeEventListener('copy', handleCopy); document.body.removeChild(textArea); } }); }; const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const fetchWithRetry = async (url, options, maxRetries = 5) => { let retries = 0; while (retries < maxRetries) { try { const response = await fetch(url, options); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json(); } catch (error) { retries++; if (retries >= maxRetries) throw error; const backoffTime = Math.pow(2, retries - 1) * 1000; await delay(backoffTime); } } }; // --- Main Application --- export default function App() { const [intention, setIntention] = useState(''); const [textType, setTextType] = useState('button'); const [includeEmoji, setIncludeEmoji] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isPolishing, setIsPolishing] = useState(false); const [results, setResults] = useState([]); const [error, setError] = useState(''); const handlePolishDraft = async () => { if (!intention.trim()) return; setIsPolishing(true); setError(''); const prompt = `Rewrite the following rough thought into a clear, concise, professional goal statement for a UX copywriter. Keep it to exactly one sentence.\n\nRough thought: "${intention}"`; const payload = { contents: [{ parts: [{ text: prompt }] }], systemInstruction: { parts: [{ text: "You are a UX product manager. Return ONLY the rewritten sentence in plain text. No quotes, no markdown." }] } }; try { const data = await fetchWithRetry( `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) } ); const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text; if (responseText) { setIntention(responseText.trim()); } } catch (err) { console.error("Failed to polish draft", err); } finally { setIsPolishing(false); } }; const handleGenerate = async () => { if (!intention.trim()) { setError("Please tell me what you want to say first!"); return; } setIsLoading(true); setError(''); setResults([]); let systemInstruction = ""; if (textType === 'button') { systemInstruction = "You are an expert UX copywriter. Generate Call-to-Action (CTA) button text. It must be short, punchy, and action-oriented (up to 5 words). Correct any errors in the user's intent."; } else if (textType === 'header') { systemInstruction = "You are an expert UX copywriter. Generate a header or title for a popup/balloon. It must be catchy, clear, and concise (3-6 words max). Correct any errors in the user's intent."; } else if (textType === 'smartTip') { systemInstruction = "You are an expert UX copywriter. Generate smart tip or tooltip text. It must be helpful, contextual, and very brief. Length should be 1-2 short sentences. Correct any errors in the user's intent."; } else if (textType === 'fullPopup') { systemInstruction = "You are an expert UX copywriter. Generate a complete popup/balloon containing a header, body text, and a CTA button. The header must be 3-6 words. The body must be 1-3 short sentences. The button must be 1-5 words. Ensure they flow logically. Correct any errors in the user's intent."; } else { systemInstruction = "You are an expert UX copywriter. Generate body text for a popup/balloon. It should be engaging, informative, easy to read, and grammatically perfect. Length should be 1-3 short, scannable sentences."; } if (includeEmoji && textType === 'smartTip') { systemInstruction += " You MUST include exactly one highly relevant emoji AT THE VERY BEGINNING of the generated text (e.g., '🚀 Your text here')."; } else { systemInstruction += " Do NOT include any emojis in the text."; } const prompt = `User's Intention/Draft: "${intention}"\n\nProvide 3 distinct options for the ${textType} text. Focus on clarity, tone, and conversion.`; let itemSchema = {}; if (textType === 'fullPopup') { itemSchema = { type: "OBJECT", properties: { header: { type: "STRING", description: "The popup header/title" }, body: { type: "STRING", description: "The popup body text" }, button: { type: "STRING", description: "The popup CTA button text" }, reasoning: { type: "STRING", description: "A brief explanation of why this combination works well" } }, required: ["header", "body", "button", "reasoning"] }; } else { itemSchema = { type: "OBJECT", properties: { text: { type: "STRING", description: "The refined text suggestion" }, reasoning: { type: "STRING", description: "A very brief explanation of why this option works well" } }, required: ["text", "reasoning"] }; } const payload = { contents: [{ parts: [{ text: prompt }] }], systemInstruction: { parts: [{ text: systemInstruction }] }, generationConfig: { responseMimeType: "application/json", responseSchema: { type: "OBJECT", properties: { options: { type: "ARRAY", items: itemSchema } }, required: ["options"] } } }; try { const data = await fetchWithRetry( `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) } ); const responseText = data.candidates?.[0]?.content?.parts?.[0]?.text; if (responseText) { const parsed = JSON.parse(responseText); setResults(parsed.options || []); } else { throw new Error("Invalid response format from AI"); } } catch (err) { console.error(err); setError("Oops! Something went wrong while generating the text. Please try again."); } finally { setIsLoading(false); } }; const types = [ { id: 'button', label: 'CTA Button', icon: MousePointerClick, desc: 'Short & Punchy (up to 5 words)' }, { id: 'header', label: 'Title / Header', icon: Heading1, desc: 'Catchy & Clear (3-6 words)' }, { id: 'body', label: 'Body Text', icon: AlignLeft, desc: 'Informative (1-3 sentences)' }, { id: 'smartTip', label: 'Smart Tip', icon: Lightbulb, desc: 'Helpful hints (1-2 sentences)' }, { id: 'fullPopup', label: 'Full Popup Set', icon: LayoutTemplate, desc: 'Header, Body & CTA Button' }, ]; return (
{/* Header Title (Mobile only, moves to sidebar on desktop) */}

Copy Refiner

Turn messy thoughts into perfect UX copy.

{/* Left Column: Inputs */}
{/* Decorative background blob */}

Copy Refiner

Turn messy thoughts into perfect UX copy.

{/* Input 1: The Intention */}