Spaces:
Running
Running
| tsx | |
| import React, { useCallback } from 'react' | |
| import { useAppStore } from '@/store/useAppStore' | |
| import { Copy, ArrowRight, CheckCircle } from 'lucide-react' | |
| /** | |
| * Component for side-by-side comparison of original and optimized prompts | |
| */ | |
| export function ComparisonView() { | |
| const { | |
| result, | |
| copiedStates, | |
| setCopiedState | |
| } = useAppStore() | |
| if (!result) return null | |
| /** | |
| * Handle copy to clipboard with feedback | |
| */ | |
| const handleCopy = useCallback(async (text: string, type: 'original' | 'optimized') => { | |
| try { | |
| await navigator.clipboard.writeText(text) | |
| setCopiedState(type, true) | |
| setTimeout(() => { | |
| setCopiedState(type, false) | |
| }, 2000) | |
| } catch (error) { | |
| console.error('Failed to copy text:', error) | |
| setCopiedState(type, true) | |
| setTimeout(() => { | |
| setCopiedState(type, false) | |
| }, 2000) | |
| } | |
| }, [setCopiedState]) | |
| /** | |
| * Calculate differences | |
| */ | |
| const originalWords = result.originalPrompt.split(/\s+/).length | |
| const optimizedWords = result.optimizedPrompt.split(/\s+/).length | |
| const wordsIncrease = optimizedWords - originalWords | |
| const wordsIncreasePercent = Math.round((wordsIncrease / originalWords) * 100) | |
| return ( | |
| <div className="space-y-6"> | |
| {/* Header */} | |
| <div className="card p-6"> | |
| <div className="flex items-center justify-between"> | |
| <h3 className="text-xl font-semibold text-gray-900 dark:text-gray-100"> | |
| Prompt-Vergleich | |
| </h3> | |
| <div className="flex items-center space-x-4 text-sm text-gray-600 dark:text-gray-400"> | |
| <span>+{wordsIncrease} Wörter (+{wordsIncreasePercent}%)</span> | |
| <div className="flex items-center space-x-1 text-green-600 dark:text-green-400"> | |
| <CheckCircle className="w-4 h-4" /> | |
| <span>Optimiert für {result.targetModel.replace('-', ' ')}</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Side-by-side comparison */} | |
| <div className="grid lg:grid-cols-2 gap-6"> | |
| {/* Original Prompt */} | |
| <div className="card p-6"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div> | |
| <h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> | |
| Original | |
| </h4> | |
| <p className="text-sm text-gray-600 dark:text-gray-400"> | |
| {result.originalPrompt.length} Zeichen, {originalWords} Wörter | |
| </p> | |
| </div> | |
| <button | |
| onClick={() => handleCopy(result.originalPrompt, 'original')} | |
| className={`flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ | |
| copiedStates.original | |
| ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' | |
| : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600' | |
| }`} | |
| > | |
| {copiedStates.original ? ( | |
| <> | |
| <CheckCircle className="w-4 h-4" /> | |
| <span>Kopiert!</span> | |
| </> | |
| ) : ( | |
| <> | |
| <Copy className="w-4 h-4" /> | |
| <span>Kopieren</span> | |
| </> | |
| )} | |
| </button> | |
| </div> | |
| <div className="relative"> | |
| <textarea | |
| value={result.originalPrompt} | |
| readOnly | |
| className="w-full h-80 p-4 bg-gray-50 dark:bg-gray-900 border border-gray-200 dark:border-gray-700 rounded-lg resize-none text-sm leading-relaxed" | |
| placeholder="Original Prompt..." | |
| /> | |
| <div className="absolute bottom-2 right-2 text-xs text-gray-500 dark:text-gray-400"> | |
| V1 | |
| </div> | |
| </div> | |
| </div> | |
| {/* Optimization Arrow */} | |
| <div className="hidden lg:flex items-center justify-center"> | |
| <div className="flex items-center space-x-4 text-primary-600 dark:text-primary-400"> | |
| <ArrowRight className="w-8 h-8" /> | |
| <div className="text-center"> | |
| <div className="text-sm font-medium">Optimiert</div> | |
| <div className="text-xs text-gray-500 dark:text-gray-400"> | |
| +{result.executionTime}ms | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Optimized Prompt */} | |
| <div className="card p-6"> | |
| <div className="flex items-center justify-between mb-4"> | |
| <div> | |
| <h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100"> | |
| Optimiert | |
| </h4> | |
| <p className="text-sm text-gray-600 dark:text-gray-400"> | |
| {result.optimizedPrompt.length} Zeichen, {optimizedWords} Wörter | |
| </p> | |
| </div> | |
| <button | |
| onClick={() => handleCopy(result.optimizedPrompt, 'optimized')} | |
| className={`flex items-center space-x-2 px-3 py-2 rounded-lg text-sm font-medium transition-all duration-200 ${ | |
| copiedStates.optimized | |
| ? 'bg-green-100 dark:bg-green-900 text-green-700 dark:text-green-300' | |
| : 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-gray-600' | |
| }`} | |
| > | |
| {copiedStates.optimized ? ( | |
| <> | |
| <CheckCircle className="w-4 h-4" /> | |
| <span>Kopiert!</span> | |
| </> | |
| ) : ( | |
| <> | |
| <Copy className="w-4 h-4" /> | |
| <span>Kopieren</span> | |
| </> | |
| )} | |
| </button> | |
| </div> | |
| <div className="relative"> | |
| <textarea | |
| value={result.optimizedPrompt} | |
| readOnly | |
| className="w-full h-80 p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700 rounded-lg resize-none text-sm leading-relaxed" | |
| placeholder="Optimierter Prompt..." | |
| /> | |
| <div className="absolute bottom-2 right-2 text-xs text-green-600 dark:text-green-400 font-medium"> | |
| V2 - Optimiert | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Mobile optimization arrow */} | |
| <div className="lg:hidden flex items-center justify-center py-4"> | |
| <div className="flex items-center space-x-2 text-primary-600 dark:text-primary-400"> | |
| <ArrowRight className="w-6 h-6 rotate-90" /> | |
| <span className="text-sm font-medium">Optimiert</span> | |
| </div> | |
| </div> | |
| {/* Improvements List */} | |
| <div className="card p-6"> | |
| <h4 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-4"> | |
| Angewendete Verbesserungen | |
| </h4> | |
| <div className="grid sm:grid-cols-2 gap-4"> | |
| {result.improvements.map((improvement, index) => ( | |
| <div key={index} className="flex items-start space-x-3 p-3 bg-green-50 dark:bg-green-900/20 rounded-lg"> | |
| <CheckCircle className="w-5 h-5 text-green-500 flex-shrink-0 mt-0.5" /> | |
| <span className="text-sm text-gray-700 dark:text-gray-300">{improvement}</span> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| {/* Final Action Button */} | |
| <div className="text-center"> | |
| <button | |
| onClick={() => handleCopy(result.optimizedPrompt, 'optimized')} | |
| className="btn-primary px-8 py-3 text-lg flex items-center space-x-2 mx-auto" | |
| > | |
| {copiedStates.optimized ? ( | |
| <> | |
| <CheckCircle className="w-5 h-5" /> | |
| <span>Kopiert!</span> | |
| </> | |
| ) : ( | |
| <> | |
| <Copy className="w-5 h-5" /> | |
| <span>Optimierten Prompt verwenden</span> | |
| </> | |
| )} | |
| </button> | |
| </div> | |
| </div> | |
| ) | |
| } | |
| </html> |