Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DocuCompare - Intelligent PDF Comparison with Qwen 3</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| :root { | |
| --primary: #6e56cf; | |
| --primary-light: #8b7adb; | |
| --primary-dark: #4a3698; | |
| --secondary: #2fb344; | |
| --dark: #1e293b; | |
| --light: #f8fafc; | |
| } | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #f1f5f9; | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%); | |
| } | |
| .dropzone { | |
| border: 2px dashed #cbd5e1; | |
| transition: all 0.3s ease; | |
| } | |
| .dropzone.active { | |
| border-color: var(--primary); | |
| background-color: rgba(110, 86, 207, 0.05); | |
| } | |
| .progress-bar { | |
| height: 6px; | |
| border-radius: 3px; | |
| background-color: #e2e8f0; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background-color: var(--primary); | |
| transition: width 0.3s ease; | |
| } | |
| .result-card { | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .result-card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| .file-chip { | |
| background-color: #f0ebff; | |
| color: var(--primary-dark); | |
| } | |
| .rotate-icon { | |
| transform: rotate(0deg); | |
| transition: transform 0.3s ease; | |
| } | |
| .rotate-icon.open { | |
| transform: rotate(180deg); | |
| } | |
| pre { | |
| white-space: pre-wrap; | |
| word-wrap: break-word; | |
| background-color: #f8fafc; | |
| border: 1px solid #e2e8f0; | |
| border-radius: 0.375rem; | |
| padding: 1rem; | |
| font-family: 'Courier New', Courier, monospace; | |
| } | |
| .loader { | |
| width: 24px; | |
| height: 24px; | |
| border: 3px solid rgba(110, 86, 207, 0.3); | |
| border-radius: 50%; | |
| border-top-color: var(--primary); | |
| animation: spin 1s ease-in-out infinite; | |
| } | |
| @keyframes spin { | |
| to { transform: rotate(360deg); } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="min-h-screen flex flex-col"> | |
| <!-- Header --> | |
| <header class="gradient-bg text-white shadow-lg"> | |
| <div class="container mx-auto px-4 py-6"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-3"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> | |
| </svg> | |
| <h1 class="text-2xl font-bold">DocuCompare AI</h1> | |
| </div> | |
| <div class="hidden md:flex items-center space-x-4"> | |
| <span class="text-white text-sm bg-white bg-opacity-20 px-3 py-1 rounded-full">Qwen 3 on HF</span> | |
| <a href="#" class="text-white hover:text-gray-200 transition">API Key</a> | |
| </div> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="flex-grow container mx-auto px-4 py-8"> | |
| <div class="max-w-5xl mx-auto"> | |
| <!-- API Key Input --> | |
| <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8"> | |
| <div class="p-6"> | |
| <h3 class="text-xl font-semibold text-slate-800 mb-2">Hugging Face API Token</h3> | |
| <div class="flex items-center space-x-2"> | |
| <input type="password" id="apiKeyInput" placeholder="Enter your HF API token (hf_...)" class="flex-grow px-4 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| <button id="saveApiKey" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition">Save</button> | |
| </div> | |
| <p class="text-sm text-slate-500 mt-2">Your token is stored locally in your browser only.</p> | |
| </div> | |
| </div> | |
| <!-- Upload Section --> | |
| <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8"> | |
| <div class="p-6 border-b border-slate-100"> | |
| <h3 class="text-xl font-semibold text-slate-800">1. Upload Document Files</h3> | |
| <p class="text-slate-500 mt-1">Upload 2 or more documents (PDF/TXT/DOCX) for Qwen 3 to compare</p> | |
| </div> | |
| <div class="p-6"> | |
| <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer"> | |
| <div class="flex flex-col items-center justify-center space-y-3"> | |
| <div class="p-4 bg-indigo-50 rounded-full"> | |
| <i data-feather="upload-cloud" class="w-8 h-8 text-indigo-500"></i> | |
| </div> | |
| <h4 class="font-medium text-slate-700">Drag & drop your files here</h4> | |
| <p class="text-sm text-slate-500">or click to browse files</p> | |
| <input type="file" id="fileInput" class="hidden" accept=".pdf,.txt,.docx" multiple> | |
| <button id="browseBtn" class="mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 transition"> | |
| Select Files | |
| </button> | |
| </div> | |
| </div> | |
| <div id="selectedFiles" class="mt-4 hidden"> | |
| <h5 class="font-medium text-slate-700 mb-2">Selected Files:</h5> | |
| <ul id="fileList" class="space-y-2"></ul> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Processing Section --> | |
| <div class="bg-white rounded-xl shadow-md overflow-hidden hidden" id="processingSection"> | |
| <div class="p-6 border-b border-slate-100"> | |
| <h3 class="text-xl font-semibold text-slate-800">2. Processing Documents with Qwen 3</h3> | |
| <p class="text-slate-500 mt-1">Qwen 3 235B is analyzing your documents</p> | |
| </div> | |
| <div class="p-6"> | |
| <div class="mb-6"> | |
| <div class="flex justify-between items-center mb-1"> | |
| <span class="text-sm font-medium text-slate-700">Progress</span> | |
| <span class="text-sm font-medium text-slate-500" id="progressText">0%</span> | |
| </div> | |
| <div class="progress-bar"> | |
| <div id="progressFill" class="progress-fill" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| <div id="statusLog" class="bg-slate-50 p-4 rounded-lg max-h-40 overflow-y-auto text-sm text-slate-600"> | |
| <div class="status-item flex items-start space-x-2 py-1"> | |
| <i data-feather="info" class="w-4 h-4 text-blue-500 mt-0.5"></i> | |
| <span>Waiting to start processing...</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Results Section --> | |
| <div class="hidden" id="resultsSection"> | |
| <h3 class="text-xl font-semibold text-slate-800 mb-4">3. Qwen 3 Analysis Results</h3> | |
| <div class="grid md:grid-cols-2 gap-6 mb-6"> | |
| <div class="result-card bg-white rounded-lg p-5 border border-slate-200 flex flex-col items-center text-center"> | |
| <div class="p-3 bg-green-50 rounded-full mb-3"> | |
| <i data-feather="git-compare" class="w-6 h-6 text-green-500"></i> | |
| </div> | |
| <h4 class="font-medium text-slate-800 mb-1">Comparison Report</h4> | |
| <p class="text-sm text-slate-500 mb-3">Detailed differences between documents</p> | |
| <button id="showComparisonBtn" class="px-3 py-1 text-sm bg-green-600 text-white rounded hover:bg-green-700 transition"> | |
| View | |
| </button> | |
| </div> | |
| <div class="result-card bg-white rounded-lg p-5 border border-slate-200 flex flex-col items-center text-center"> | |
| <div class="p-3 bg-purple-50 rounded-full mb-3"> | |
| <i data-feather="check-circle" class="w-6 h-6 text-purple-500"></i> | |
| </div> | |
| <h4 class="font-medium text-slate-800 mb-1">Validation Insights</h4> | |
| <p class="text-sm text-slate-500 mb-3">Accuracy and validation analysis</p> | |
| <button id="showValidationBtn" class="px-3 py-1 text-sm bg-purple-600 text-white rounded hover:bg-purple-700 transition"> | |
| View | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8"> | |
| <div id="comparisonResults" class="hidden p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h4 class="text-lg font-semibold text-slate-800">Qwen 3 Comparison Report</h4> | |
| <div class="flex space-x-2"> | |
| <button id="copyComparisonBtn" class="flex items-center space-x-1 px-3 py-1 text-sm bg-slate-100 text-slate-600 rounded hover:bg-slate-200 transition"> | |
| <i data-feather="copy" class="w-4 h-4"></i> | |
| <span>Copy</span> | |
| </button> | |
| <button id="downloadComparisonBtn" class="flex items-center space-x-1 px-3 py-1 text-sm bg-slate-100 text-slate-600 rounded hover:bg-slate-200 transition"> | |
| <i data-feather="download" class="w-4 h-4"></i> | |
| <span>Download</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="comparisonOutput" class="whitespace-pre-wrap text-sm text-slate-700"></div> | |
| </div> | |
| <div id="validationResults" class="hidden p-6"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h4 class="text-lg font-semibold text-slate-800">Qwen 3 Validation Insights</h4> | |
| <div class="flex space-x-2"> | |
| <button id="copyValidationBtn" class="flex items-center space-x-1 px-3 py-1 text-sm bg-slate-100 text-slate-600 rounded hover:bg-slate-200 transition"> | |
| <i data-feather="copy" class="w-4 h-4"></i> | |
| <span>Copy</span> | |
| </button> | |
| <button id="downloadValidationBtn" class="flex items-center space-x-1 px-3 py-1 text-sm bg-slate-100 text-slate-600 rounded hover:bg-slate-200 transition"> | |
| <i data-feather="download" class="w-4 h-4"></i> | |
| <span>Download</span> | |
| </button> | |
| </div> | |
| </div> | |
| <div id="validationOutput" class="whitespace-pre-wrap text-sm text-slate-700"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Footer --> | |
| <footer class="bg-white border-t border-slate-200 py-6"> | |
| <div class="container mx-auto px-4"> | |
| <div class="flex flex-col md:flex-row justify-between items-center"> | |
| <div class="flex items-center space-x-2 mb-4 md:mb-0"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> | |
| </svg> | |
| <span class="font-medium text-slate-800">DocuCompare AI</span> | |
| </div> | |
| <div class="text-sm text-slate-500 text-center md:text-right"> | |
| <p>Powered by Qwen 3 via Hugging Face Endpoints</p> | |
| </div> | |
| </div> | |
| </div> | |
| </footer> | |
| </div> | |
| <script> | |
| // Initialize feather icons | |
| document.addEventListener('DOMContentLoaded', function() { | |
| feather.replace(); | |
| // System prompts for Qwen 3 | |
| const EXTRACTION_PROMPT = `You are a professional document analyst. Extract the following from the provided documents: | |
| 1. Document metadata (parties, dates, references) | |
| 2. Quantitative data (line items, amounts, dates) | |
| 3. Key terms and conditions | |
| 4. Any special provisions`; | |
| const COMPARISON_PROMPT = `Compare these document extracts and highlight: | |
| 1. Major differences in terms and pricing | |
| 2. Variances in scope/specifications | |
| 3. Advantages/disadvantages of each document | |
| 4. Any red flags or concerns | |
| Format as detailed markdown with tables where appropriate`; | |
| const VALIDATION_PROMPT = `Validate this comparison report: | |
| 1. Check for accuracy against original docs | |
| 2. Identify missing comparisons | |
| 3. Note any possible misinterpretations | |
| 4. Suggest additional areas that need review`; | |
| // DOM Elements | |
| const apiKeyInput = document.getElementById('apiKeyInput'); | |
| const saveApiKey = document.getElementById('saveApiKey'); | |
| const dropzone = document.getElementById('dropzone'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const browseBtn = document.getElementById('browseBtn'); | |
| const selectedFiles = document.getElementById('selectedFiles'); | |
| const fileList = document.getElementById('fileList'); | |
| const processingSection = document.getElementById('processingSection'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const progressText = document.getElementById('progressText'); | |
| const progressFill = document.getElementById('progressFill'); | |
| const statusLog = document.getElementById('statusLog'); | |
| const comparisonResults = document.getElementById('comparisonResults'); | |
| const validationResults = document.getElementById('validationResults'); | |
| const comparisonOutput = document.getElementById('comparisonOutput'); | |
| const validationOutput = document.getElementById('validationOutput'); | |
| // Check for saved API key | |
| const savedApiKey = localStorage.getItem('hfApiToken'); | |
| if (savedApiKey) { | |
| apiKeyInput.value = savedApiKey; | |
| } | |
| // Event Listeners | |
| saveApiKey.addEventListener('click', () => { | |
| const key = apiKeyInput.value.trim(); | |
| if (key && key.startsWith('hf_')) { | |
| localStorage.setItem('hfApiToken', key); | |
| addStatusLog("HF API token saved locally in your browser.", "success"); | |
| } else { | |
| addStatusLog("Please enter a valid HF token (starting with hf_)", "error"); | |
| apiKeyInput.focus(); | |
| } | |
| }); | |
| browseBtn.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', handleFileSelect); | |
| // Drag and drop handlers | |
| dropzone.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| dropzone.classList.add('active'); | |
| }); | |
| dropzone.addEventListener('dragleave', () => { | |
| dropzone.classList.remove('active'); | |
| }); | |
| dropzone.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| dropzone.classList.remove('active'); | |
| fileInput.files = e.dataTransfer.files; | |
| handleFileSelect(); | |
| }); | |
| // Result navigation buttons | |
| document.getElementById('showComparisonBtn').addEventListener('click', () => { | |
| comparisonResults.classList.remove('hidden'); | |
| validationResults.classList.add('hidden'); | |
| }); | |
| document.getElementById('showValidationBtn').addEventListener('click', () => { | |
| comparisonResults.classList.add('hidden'); | |
| validationResults.classList.remove('hidden'); | |
| }); | |
| // Copy/download buttons | |
| document.getElementById('copyComparisonBtn').addEventListener('click', copyComparison); | |
| document.getElementById('copyValidationBtn').addEventListener('click', copyValidation); | |
| document.getElementById('downloadComparisonBtn').addEventListener('click', downloadComparison); | |
| document.getElementById('downloadValidationBtn').addEventListener('click', downloadValidation); | |
| // Functions | |
| function handleFileSelect() { | |
| const files = fileInput.files; | |
| if (files.length < 2) { | |
| addStatusLog('Please select at least 2 files for comparison.', 'error'); | |
| return; | |
| } | |
| // Show selected files | |
| fileList.innerHTML = ''; | |
| Array.from(files).forEach(file => { | |
| const li = document.createElement('li'); | |
| li.className = 'flex items-center justify-between bg-slate-50 px-3 py-2 rounded'; | |
| li.innerHTML = ` | |
| <div class="flex items-center space-x-3"> | |
| <i data-feather="file" class="w-4 h-4 text-slate-500"></i> | |
| <span class="text-sm text-slate-700 truncate max-w-xs">${file.name}</span> | |
| </div> | |
| <span class="text-xs text-slate-500">${formatFileSize(file.size)}</span> | |
| `; | |
| fileList.appendChild(li); | |
| }); | |
| selectedFiles.classList.remove('hidden'); | |
| // Start processing after a slight delay for UI to update | |
| setTimeout(startProcessing, 1000); | |
| feather.replace(); | |
| } | |
| function formatFileSize(bytes) { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| } | |
| function startProcessing() { | |
| const apiKey = localStorage.getItem('hfApiToken'); | |
| if (!apiKey) { | |
| addStatusLog('Please save your Hugging Face API token first.', 'error'); | |
| return; | |
| } | |
| processingSection.classList.remove('hidden'); | |
| addStatusLog('Starting document processing with Qwen 3...'); | |
| // Simulate processing with progress updates | |
| simulateProcessing(); | |
| } | |
| function simulateHFQwen3Call(prompt, content, callback) { | |
| // In a real implementation, this would call the HF Inference API: | |
| /* | |
| fetch('https://router.huggingface.co/fireworks-ai/inference/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': `Bearer ${apiKey}`, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| messages: [{ | |
| role: "user", | |
| content: `${prompt}\n\n${content}` | |
| }], | |
| model: "accounts/fireworks/models/qwen3-235b-a22b", | |
| stream: false | |
| }) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => callback(data.choices[0].message.content)) | |
| .catch(error => callback(`Error: ${error.message}`)); | |
| */ | |
| // For demo purposes, simulate the API response | |
| setTimeout(() => { | |
| if (prompt === COMPARISON_PROMPT) { | |
| callback(`## Comparison Report (Simulated) | |
| ### Key Findings | |
| 1. **Price Variation**: Document A shows consistently higher pricing (avg +18%) than Document B | |
| 2. **Scope Differences**: Document B includes additional services (maintenance, support) not in A | |
| 3. **Timeline**: Document A proposes 6 month timeline vs Document B's 4 month estimate | |
| | Category | Document A | Document B | Difference | | |
| |----------------|------------|------------|------------| | |
| | Development | $28,500 | $24,000 | +18.75% | | |
| | Testing | $9,200 | $7,500 | +22.67% | | |
| | Maintenance | - | $3,000 | N/A | | |
| **Recommendation**: Consider negotiating with Provider A to match Provider B's pricing or switch to Provider B for lower costs and additional services.`); | |
| } else if (prompt === VALIDATION_PROMPT) { | |
| callback(`## Validation Report (Simulated) | |
| ### Verification Points | |
| ✅ Pricing differences confirmed across all line items | |
| ✅ Document B does indeed include maintenance services | |
| ⚠️ Timeline estimates should be validated with both providers | |
| ### Additional Findings | |
| 1. Payment terms weren't compared (50% upfront in both) | |
| 2. Penalty clauses differ (5% vs 10% for delays) | |
| 3. Document A includes better IP protections | |
| **Action Items**: | |
| 1. Verify actual delivery capacity for timeline claims | |
| 2. Compare quality guarantees between providers`); | |
| } else { | |
| callback(`Error: Unsupported prompt type`); | |
| } | |
| }, 2000); | |
| } | |
| function simulateProcessing() { | |
| let progress = 0; | |
| const steps = [ | |
| {text: "Uploading documents...", increment: 15}, | |
| {text: "Extracting content from files...", increment: 25}, | |
| {text: "Sending to Qwen 3 via HF endpoint...", increment: 30}, | |
| {text: "Analyzing document differences...", increment: 15}, | |
| {text: "Generating validation report...", increment: 10}, | |
| {text: "Finalizing results...", increment: 5} | |
| ]; | |
| let currentStep = 0; | |
| const processInterval = setInterval(() => { | |
| if (currentStep < steps.length) { | |
| const step = steps[currentStep]; | |
| // Update progress | |
| progress = Math.min(progress + step.increment, 100); | |
| progressText.textContent = `${progress}%`; | |
| progressFill.style.width = `${progress}%`; | |
| // Add status log entry | |
| addStatusLog(step.text); | |
| // Simulate API calls during processing | |
| if (currentStep === 2) { | |
| const mockContent = ` | |
| Document A: Vendor Alpha Proposal | |
| - Development: $28,500 | |
| - Testing: $9,200 | |
| - Timeline: 6 months | |
| - Payment: 50% upfront | |
| Document B: Supplier Beta Offer | |
| - Development: $24,000 | |
| - Testing: $7,500 | |
| - Maintenance: $3,000 | |
| - Timeline: 4 months | |
| - Payment: 50% upfront`; | |
| simulateHFQwen3Call(COMPARISON_PROMPT, mockContent, (result) => { | |
| comparisonOutput.textContent = result; | |
| addStatusLog('Comparison report generated successfully', 'success'); | |
| }); | |
| simulateHFQwen3Call(VALIDATION_PROMPT, mockContent, (result) => { | |
| validationOutput.textContent = result; | |
| addStatusLog('Validation analysis complete', 'success'); | |
| }); | |
| } | |
| currentStep++; | |
| } else { | |
| clearInterval(processInterval); | |
| // Add completion status | |
| addStatusLog("Analysis complete! View results below.", "success"); | |
| // Show results after a delay | |
| setTimeout(showResults, 1000); | |
| } | |
| }, 1500); | |
| } | |
| function addStatusLog(message, type = "info") { | |
| const statusItem = document.createElement('div'); | |
| statusItem.className = 'status-item flex items-start space-x-2 py-1'; | |
| let icon; | |
| let textColor; | |
| switch(type) { | |
| case "success": | |
| icon = 'check-circle'; | |
| textColor = 'text-green-500'; | |
| break; | |
| case "error": | |
| icon = 'alert-circle'; | |
| textColor = 'text-red-500'; | |
| break; | |
| case "warning": | |
| icon = 'alert-triangle'; | |
| textColor = 'text-yellow-500'; | |
| break; | |
| default: | |
| icon = 'info'; | |
| textColor = 'text-blue-500'; | |
| } | |
| statusItem.innerHTML = ` | |
| <i data-feather="${icon}" class="w-4 h-4 ${textColor} mt-0.5"></i> | |
| <span>${message}</span> | |
| `; | |
| statusLog.appendChild(statusItem); | |
| statusLog.scrollTop = statusLog.scrollHeight; | |
| feather.replace(); | |
| } | |
| function showResults() { | |
| processingSection.classList.add('hidden'); | |
| resultsSection.classList.remove('hidden'); | |
| // Show comparison by default | |
| comparisonResults.classList.remove('hidden'); | |
| validationResults.classList.add('hidden'); | |
| } | |
| // Clipboard functions | |
| function copyComparison() { | |
| navigator.clipboard.writeText(comparisonOutput.textContent); | |
| showCopyFeedback('copyComparisonBtn'); | |
| } | |
| function copyValidation() { | |
| navigator.clipboard.writeText(validationOutput.textContent); | |
| showCopyFeedback('copyValidationBtn'); | |
| } | |
| function showCopyFeedback(buttonId) { | |
| const button = document.getElementById(buttonId); | |
| const icon = button.querySelector('i'); | |
| const text = button.querySelector('span'); | |
| // Change to indicate success | |
| icon.setAttribute('data-feather', 'check'); | |
| feather.replace(); | |
| text.textContent = 'Copied'; | |
| // Reset after 2 seconds | |
| setTimeout(() => { | |
| icon.setAttribute('data-feather', 'copy'); | |
| feather.replace(); | |
| text.textContent = 'Copy'; | |
| }, 2000); | |
| } | |
| // Download functions | |
| function downloadComparison() { | |
| downloadFile('Qwen3_Comparison_Report.md', comparisonOutput.textContent); | |
| } | |
| function downloadValidation() { | |
| downloadFile('Qwen3_Validation_Report.md', validationOutput.textContent); | |
| } | |
| function downloadFile(filename, content) { | |
| const blob = new Blob([content], {type: 'text/markdown'}); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = filename; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Ultronprime/doccompare" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |