Powerpoint_AI / components /editor /AIToolsDialog.tsx
Reubencf's picture
Readme updated
45b3fab
/*
* AIToolsDialog.tsx
* Purpose: Shows the focused text-editing dialog for refine/change/expand/regenerate AI actions.
* Used by: GoogleSlidesEditor when a text element is edited with AI.
* Depends on: AI edit route and editor apply callbacks.
*/
import React, { useState } from 'react';
import { X, Sparkles, RefreshCw, FileText, Maximize2 } from 'lucide-react';
interface AIToolsDialogProps {
isOpen: boolean;
onClose: () => void;
selectedText: string;
onApply: (newText: string) => void;
onAIEdit: (action: 'refine' | 'change' | 'expand') => Promise<string>;
}
export default function AIToolsDialog({
isOpen,
onClose,
selectedText,
onApply,
onAIEdit
}: AIToolsDialogProps) {
const [currentText, setCurrentText] = useState(selectedText);
const [isProcessing, setIsProcessing] = useState(false);
const [selectedAction, setSelectedAction] = useState<'refine' | 'change' | 'expand' | null>(null);
if (!isOpen) return null;
const handleAction = async (action: 'refine' | 'change' | 'expand') => {
setIsProcessing(true);
setSelectedAction(action);
try {
const newText = await onAIEdit(action);
setCurrentText(newText);
} catch (error) {
console.error('AI edit error:', error);
} finally {
setIsProcessing(false);
setSelectedAction(null);
}
};
const handleApply = () => {
onApply(currentText);
onClose();
};
const handleCancel = () => {
setCurrentText(selectedText);
onClose();
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-[1000]">
<div className="bg-white rounded-lg shadow-xl w-[600px] max-h-[80vh] flex flex-col">
{/* Header */}
<div className="flex items-center justify-between p-4 border-b">
<div className="flex items-center gap-2">
<Sparkles className="w-5 h-5 text-purple-600" />
<h2 className="text-lg font-semibold">AI Text Tools</h2>
</div>
<button
onClick={handleCancel}
className="p-1 hover:bg-gray-100 rounded transition-colors"
>
<X className="w-5 h-5" />
</button>
</div>
{/* Content */}
<div className="flex-1 overflow-auto p-4">
<div className="space-y-4">
{/* Action buttons */}
<div className="grid grid-cols-3 gap-3">
<button
onClick={() => handleAction('refine')}
disabled={isProcessing}
className={`
p-4 rounded-lg border-2 transition-all
${isProcessing && selectedAction === 'refine'
? 'border-purple-500 bg-purple-50'
: 'border-gray-200 hover:border-purple-400 hover:bg-purple-50'}
${isProcessing ? 'cursor-not-allowed opacity-75' : 'cursor-pointer'}
`}
>
<div className="flex flex-col items-center gap-2">
{isProcessing && selectedAction === 'refine' ? (
<div className="animate-spin h-6 w-6 border-2 border-purple-600 border-t-transparent rounded-full" />
) : (
<Sparkles className="w-6 h-6 text-purple-600" />
)}
<div className="text-sm font-medium">Refine Text</div>
<div className="text-xs text-gray-500 text-center">Make it more professional</div>
</div>
</button>
<button
onClick={() => handleAction('change')}
disabled={isProcessing}
className={`
p-4 rounded-lg border-2 transition-all
${isProcessing && selectedAction === 'change'
? 'border-purple-500 bg-purple-50'
: 'border-gray-200 hover:border-purple-400 hover:bg-purple-50'}
${isProcessing ? 'cursor-not-allowed opacity-75' : 'cursor-pointer'}
`}
>
<div className="flex flex-col items-center gap-2">
{isProcessing && selectedAction === 'change' ? (
<div className="animate-spin h-6 w-6 border-2 border-purple-600 border-t-transparent rounded-full" />
) : (
<RefreshCw className="w-6 h-6 text-purple-600" />
)}
<div className="text-sm font-medium">Rewrite</div>
<div className="text-xs text-gray-500 text-center">Different wording, same meaning</div>
</div>
</button>
<button
onClick={() => handleAction('expand')}
disabled={isProcessing}
className={`
p-4 rounded-lg border-2 transition-all
${isProcessing && selectedAction === 'expand'
? 'border-purple-500 bg-purple-50'
: 'border-gray-200 hover:border-purple-400 hover:bg-purple-50'}
${isProcessing ? 'cursor-not-allowed opacity-75' : 'cursor-pointer'}
`}
>
<div className="flex flex-col items-center gap-2">
{isProcessing && selectedAction === 'expand' ? (
<div className="animate-spin h-6 w-6 border-2 border-purple-600 border-t-transparent rounded-full" />
) : (
<Maximize2 className="w-6 h-6 text-purple-600" />
)}
<div className="text-sm font-medium">Expand</div>
<div className="text-xs text-gray-500 text-center">Add more detail</div>
</div>
</button>
</div>
{/* Text preview */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Preview
</label>
<div className="relative">
<textarea
value={currentText}
onChange={(e) => setCurrentText(e.target.value)}
className="w-full h-40 p-3 border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-purple-500"
placeholder="Your text will appear here..."
/>
{isProcessing && (
<div className="absolute inset-0 bg-white bg-opacity-75 flex items-center justify-center rounded-lg">
<div className="flex items-center gap-2">
<div className="animate-spin h-5 w-5 border-2 border-purple-600 border-t-transparent rounded-full" />
<span className="text-sm text-gray-600">AI is working...</span>
</div>
</div>
)}
</div>
<div className="mt-2 flex items-center justify-between text-xs text-gray-500">
<span>{currentText.split(' ').length} words</span>
<span>{currentText.length} characters</span>
</div>
</div>
{/* Comparison */}
{currentText !== selectedText && (
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
<div className="flex items-start gap-2">
<FileText className="w-4 h-4 text-yellow-600 mt-0.5" />
<div className="flex-1">
<div className="text-sm font-medium text-yellow-800">Changes made</div>
<div className="text-xs text-yellow-700 mt-1">
The text has been modified. Click "Apply Changes" to save or "Cancel" to revert.
</div>
</div>
</div>
</div>
)}
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-end gap-3 p-4 border-t">
<button
onClick={handleCancel}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
>
Cancel
</button>
<button
onClick={handleApply}
disabled={isProcessing}
className="px-4 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
Apply Changes
</button>
</div>
</div>
</div>
);
}