Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Sentiment Analysis</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen flex items-center justify-center font-sans p-4"> | |
| <div class="container mx-auto max-w-2xl w-full p-8 bg-gray-800 rounded-2xl shadow-2xl border border-gray-700"> | |
| <h1 class="text-4xl font-bold text-center mb-2 text-cyan-400"> | |
| Sentiment Analysis Engine | |
| </h1> | |
| <p id="modelName" class="text-center text-gray-400 mb-8 h-6"> | |
| (Loading model...) | |
| </p> | |
| <div id="errorDiv" class="hidden p-3 mb-4 bg-red-800 border border-red-600 text-red-100 rounded-lg"> | |
| <span id="errorMessage"></span> | |
| </div> | |
| <div class="mb-6 relative"> | |
| <label for="textInput" class="block text-lg font-medium text-gray-300 mb-2"> | |
| Enter your text: | |
| </label> | |
| <textarea id="textInput" rows="5" | |
| class="w-full p-4 bg-gray-700 border border-gray-600 rounded-lg text-white text-base focus:ring-2 focus:ring-cyan-500 focus:outline-none transition duration-200" | |
| placeholder="Type something... (e.g., 'I love this product!')"></textarea> | |
| </div> | |
| <div class="flex flex-col sm:flex-row gap-4"> | |
| <button id="analyzeButton" | |
| class="w-full bg-cyan-600 hover:bg-cyan-500 text-white text-lg font-bold py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 flex items-center justify-center"> | |
| <svg id="spinner" class="animate-spin -ml-1 mr-3 h-5 w-5 text-white hidden" | |
| xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | |
| <path class="opacity-75" fill="currentColor" | |
| d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"> | |
| </path> | |
| </svg> | |
| <span id="buttonText">Analyze Sentiment</span> | |
| </button> | |
| <button id="clearButton" | |
| class="w-full sm:w-auto bg-gray-600 hover:bg-gray-500 text-white font-bold py-3 px-6 rounded-lg transition duration-300"> | |
| Clear | |
| </button> | |
| </div> | |
| <div id="result" class="mt-8 p-6 bg-gray-700 rounded-lg hidden border border-gray-600"> | |
| <h2 class="text-2xl font-semibold mb-4 text-gray-200">Analysis Result:</h2> | |
| <div id="sentiment" class="text-4xl font-extrabold text-center flex items-center justify-center gap-4"> | |
| <span id="sentimentIcon"></span> | |
| <span id="sentimentText"></span> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Select all DOM elements | |
| const textInput = document.getElementById('textInput'); | |
| const analyzeButton = document.getElementById('analyzeButton'); | |
| const clearButton = document.getElementById('clearButton'); | |
| const resultDiv = document.getElementById('result'); | |
| const sentimentText = document.getElementById('sentimentText'); | |
| const sentimentIcon = document.getElementById('sentimentIcon'); | |
| const modelNameEl = document.getElementById('modelName'); | |
| const errorDiv = document.getElementById('errorDiv'); | |
| const errorMessage = document.getElementById('errorMessage'); | |
| const spinner = document.getElementById('spinner'); | |
| const buttonText = document.getElementById('buttonText'); | |
| // API endpoints | |
| const predictApi = 'https://anis80-sentiment-analysis-api.hf.space/predict'; | |
| const statusApi = 'https://anis80-sentiment-analysis-api.hf.space/status'; | |
| // Icon SVGs | |
| const icons = { | |
| positive: `<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-green-400" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" /></svg>`, | |
| negative: `<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-red-400" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" /></svg>`, | |
| neutral: `<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-yellow-400" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM7 10a1 1 0 11-2 0 1 1 0 012 0zm7 0a1 1 0 11-2 0 1 1 0 012 0z" clip-rule="evenodd" /></svg>`, | |
| other: `<svg xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 text-blue-400" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd" /></svg>` | |
| }; | |
| // Fetch model status on page load | |
| window.addEventListener('load', async () => { | |
| try { | |
| const response = await fetch(statusApi); | |
| if (!response.ok) throw new Error('Status check failed'); | |
| const data = await response.json(); | |
| modelNameEl.textContent = `(Active Model: ${data.model_name || 'N/A'})`; | |
| } catch (error) { | |
| modelNameEl.textContent = '(Could not load model status)'; | |
| console.error('Error fetching status:', error); | |
| } | |
| }); | |
| // Analyze button click event | |
| analyzeButton.addEventListener('click', async () => { | |
| const text = textInput.value; | |
| if (!text) { | |
| showError('Please enter some text to analyze.'); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| const response = await fetch(predictApi, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text: text }) | |
| }); | |
| let data; | |
| const contentType = response.headers.get("content-type"); | |
| if (contentType && contentType.indexOf("application/json") !== -1) { | |
| data = await response.json(); | |
| } else { | |
| // If not JSON, probably an error page | |
| const text = await response.text(); | |
| console.error('Non-JSON response:', text); | |
| if (!response.ok) { | |
| throw new Error('Server Error: ' + response.status + ' ' + response.statusText); | |
| } | |
| data = {}; // safe fallback | |
| } | |
| if (!response.ok) { | |
| throw new Error(data.detail || 'Network response was not ok'); | |
| } | |
| if (data.predicted_sentiment) { | |
| displayResult(data.predicted_sentiment); | |
| } else if (data.error) { | |
| showError('Error from server: ' + data.error); | |
| } | |
| } catch (error) { | |
| console.error('Fetch error:', error); | |
| let errorMsg = 'Could not connect to the analysis server.'; | |
| if (error.message) { | |
| errorMsg += ' Details: ' + error.message; | |
| } | |
| if (window.location.protocol === 'https:' && predictApi.startsWith('http:')) { | |
| errorMsg += ' (Mixed Content Error: Cannot access HTTP API from HTTPS page)'; | |
| } | |
| showError(errorMsg + ' Check console for more info.'); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }); | |
| // Clear button click event | |
| clearButton.addEventListener('click', () => { | |
| textInput.value = ''; | |
| resultDiv.classList.add('hidden'); | |
| errorDiv.classList.add('hidden'); | |
| }); | |
| // Function to manage loading state | |
| function setLoading(isLoading) { | |
| analyzeButton.disabled = isLoading; | |
| if (isLoading) { | |
| spinner.classList.remove('hidden'); | |
| buttonText.textContent = 'Analyzing...'; | |
| } else { | |
| spinner.classList.add('hidden'); | |
| buttonText.textContent = 'Analyze Sentiment'; | |
| } | |
| } | |
| // Function to show error messages | |
| function showError(message) { | |
| errorMessage.textContent = message; | |
| errorDiv.classList.remove('hidden'); | |
| resultDiv.classList.add('hidden'); | |
| } | |
| // Function to display results | |
| function displayResult(sentiment) { | |
| sentimentText.textContent = sentiment; | |
| const sentimentKey = sentiment.toLowerCase(); | |
| let iconSvg = icons.other; // Default | |
| let colorClass = 'text-blue-400'; | |
| if (sentimentKey.includes('positive')) { | |
| iconSvg = icons.positive; | |
| colorClass = 'text-green-400'; | |
| } else if (sentimentKey.includes('negative')) { | |
| iconSvg = icons.negative; | |
| colorClass = 'text-red-400'; | |
| } else if (sentimentKey.includes('neutral')) { | |
| iconSvg = icons.neutral; | |
| colorClass = 'text-yellow-400'; | |
| } | |
| sentimentIcon.innerHTML = iconSvg; | |
| sentimentText.className = colorClass; // Only set color class | |
| resultDiv.classList.remove('hidden'); | |
| errorDiv.classList.add('hidden'); | |
| } | |
| </script> | |
| </body> | |
| </html> |