Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>TexClarity</title> | |
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
<link href="https://fonts.googleapis.com/css2?family=Griffy&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
background: linear-gradient(135deg, #000, #1a1a2e); | |
color: #fff; | |
margin: 0; | |
padding: 20px; | |
display: flex; | |
justify-content: center; | |
min-height: 100vh; | |
} | |
.container { | |
max-width: 1000px; | |
width: 100%; | |
} | |
h1 { | |
font-family: 'Griffy', cursive; | |
font-size: 2em; | |
margin-bottom: 0; | |
margin-top: 10px; | |
position: absolute; | |
top: 20px; | |
left: 20px; | |
color: #fff; | |
text-align: left; | |
box-shadow: 0 0 5px rgba(78, 121, 230, 0.2); | |
padding: 5px 10px; | |
border-radius: 5px; | |
animation: glow 2s infinite; | |
} | |
@keyframes glow { | |
0% { box-shadow: 0 0 5px rgba(78, 121, 230, 0.2); } | |
50% { box-shadow: 0 0 8px rgba(78, 121, 230, 0.4); } | |
100% { box-shadow: 0 0 5px rgba(78, 121, 230, 0.2); } | |
} | |
h2 { | |
font-family: 'Verdana', sans-serif; | |
font-size: 1.8em; | |
font-weight: normal; | |
margin-top: 30px; | |
} | |
h1, h2 { | |
color: #fff; | |
text-align: left; | |
} | |
.input-section { | |
margin-top: 60px; | |
margin-bottom: 20px; | |
} | |
textarea { | |
width: 100%; | |
height: 250px; | |
padding: 15px; | |
margin-bottom: 10px; | |
border: none; | |
border-radius: 8px; | |
background: #e0e0e0; | |
color: #333; | |
font-size: 1.2em; | |
box-sizing: border-box; | |
box-shadow: 0 0 10px rgba(78, 121, 230, 0.3); | |
transition: box-shadow 0.3s; | |
} | |
textarea:focus { | |
box-shadow: 0 0 15px rgba(78, 121, 230, 0.6); | |
} | |
.labels-section { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
margin-bottom: 10px; | |
} | |
.labels-section label { | |
font-size: 1.2em; | |
} | |
input { | |
flex: 2; | |
padding: 15px; | |
border: 2px solid #4e79e6; | |
border-radius: 8px; | |
background: #e0e0e0; | |
color: #333; | |
font-size: 1.2em; | |
box-shadow: 0 0 8px rgba(78, 121, 230, 0.3); | |
transition: box-shadow 0.3s; | |
} | |
input:focus { | |
box-shadow: 0 0 8px rgba(78, 121, 230, 0.5); | |
} | |
button { | |
background: linear-gradient(135deg, #2c5aa0, #4e79e6); | |
color: #fff; | |
padding: 10px 20px; | |
border: none; | |
border-radius: 8px; | |
cursor: pointer; | |
font-size: 1.1em; | |
box-shadow: 0 0 15px rgba(78, 121, 230, 0.5); | |
animation: pulse 2s infinite; | |
transition: transform 0.2s; | |
} | |
button:hover { | |
transform: scale(1.05); | |
box-shadow: 0 0 20px rgba(78, 121, 230, 0.8); | |
} | |
@keyframes pulse { | |
0% { box-shadow: 0 0 15px rgba(78, 121, 230, 0.5); } | |
50% { box-shadow: 0 0 25px rgba(78, 121, 230, 0.8); } | |
100% { box-shadow: 0 0 15px rgba(78, 121, 230, 0.5); } | |
} | |
.results { | |
display: flex; | |
flex-direction: column; | |
gap: 20px; | |
} | |
table { | |
width: 100%; | |
border-collapse: collapse; | |
background: linear-gradient(135deg, #000, #1a1a2e); | |
border-radius: 8px; | |
overflow: hidden; | |
} | |
th, td { | |
padding: 12px; | |
text-align: left; | |
color: #fff; | |
} | |
th { | |
font-weight: bold; | |
} | |
.chart-container { | |
display: none; | |
position: relative; | |
height: 300px; | |
background: linear-gradient(135deg, #000, #1a1a2e); | |
border-radius: 8px; | |
padding: 10px; | |
} | |
.loading { | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
color: #4e79e6; | |
font-size: 3.5em; | |
display: none; | |
} | |
.loading::after { | |
content: '...'; | |
animation: dots 1.5s infinite; | |
} | |
@keyframes dots { | |
0% { content: '.'; } | |
33% { content: '..'; } | |
66% { content: '...'; } | |
} | |
@media (max-width: 768px) { | |
.container { | |
padding: 10px; | |
} | |
textarea, input, button { | |
font-size: 1em; | |
} | |
.labels-section { | |
flex-direction: column; | |
align-items: flex-start; | |
} | |
button { | |
width: 100%; | |
} | |
h1 { | |
font-size: 1.5em; | |
top: 10px; | |
left: 10px; | |
} | |
h2 { | |
margin-top: 20px; | |
font-size: 1.4em; | |
} | |
.input-section { | |
margin-top: 40px; | |
} | |
.loading { | |
font-size: 2.5em; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>TexClarity</h1> | |
<h2>Make your text analysis clearer</h2> | |
<div class="input-section"> | |
<textarea id="document" placeholder="Enter your text here..."></textarea> | |
<div class="labels-section"> | |
<label>Labels:</label> | |
<input type="text" id="labels" placeholder="Enter labels (comma-separated)"> | |
<button id="classifyBtn">Classify</button> | |
</div> | |
</div> | |
<div class="results"> | |
<table id="results-table"> | |
<thead> | |
<tr> | |
<th>Label</th> | |
<th>Score</th> | |
</tr> | |
</thead> | |
<tbody></tbody> | |
</table> | |
<div class="chart-container"> | |
<canvas id="resultsChart"></canvas> | |
</div> | |
</div> | |
</div> | |
<div id="loading" class="loading"></div> | |
<script> | |
let chartInstance = null; | |
const textarea = document.getElementById('document'); | |
textarea.addEventListener('focus', function() { | |
if (this.value === this.getAttribute('placeholder')) { | |
this.value = ''; | |
} | |
}); | |
textarea.addEventListener('blur', function() { | |
if (!this.value) { | |
this.value = this.getAttribute('placeholder'); | |
} | |
}); | |
document.getElementById("classifyBtn").addEventListener("click", classifyText); | |
async function classifyText() { | |
const textInput = document.getElementById('document').value; | |
const labelsInput = document.getElementById('labels').value; | |
const loadingElement = document.getElementById('loading'); | |
const chartContainer = document.querySelector('.chart-container'); | |
// Validate inputs | |
if (!textInput || textInput === textarea.getAttribute('placeholder')) { | |
alert('Please enter some text to classify.'); | |
return; | |
} | |
if (!labelsInput) { | |
alert('Please enter at least one label.'); | |
return; | |
} | |
// Keep labels as a comma-separated string (as expected by the backend) | |
const labels = labelsInput.trim(); | |
console.log("Sending request with text:", textInput.substring(0, 50) + "..."); | |
console.log("Labels:", labels); | |
loadingElement.style.display = 'block'; | |
chartContainer.style.display = 'none'; | |
try { | |
const response = await fetch('/classify', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify({ document: textInput, labels: labels }), | |
}); | |
console.log("Response status:", response.status); | |
if (!response.ok) { | |
const errorText = await response.text(); | |
throw new Error(`Classification failed with status ${response.status}: ${errorText}`); | |
} | |
const rawResults = await response.json(); | |
console.log("Raw response:", JSON.stringify(rawResults, null, 2)); // Formatted JSON | |
// Handle both array and object responses | |
let results; | |
if (Array.isArray(rawResults)) { | |
results = rawResults[0]; | |
} else { | |
results = rawResults; | |
} | |
console.log("Processed results:", JSON.stringify(results, null, 2)); // Formatted JSON | |
if (results.error) { | |
throw new Error(results.error); | |
} | |
if (!results.labels || !results.scores) { | |
throw new Error("Invalid response format: 'labels' or 'scores' missing"); | |
} | |
updateTable(results); | |
updateChart(results); | |
chartContainer.style.display = 'block'; | |
} catch (error) { | |
console.error("Error during classification:", error); | |
alert('Error: ' + error.message); | |
} finally { | |
loadingElement.style.display = 'none'; | |
} | |
} | |
function updateTable(results) { | |
const tbody = document.querySelector('#results-table tbody'); | |
tbody.innerHTML = ''; | |
// If labels is a single string, split it manually based on the input | |
let labelsArray = []; | |
if (typeof results.labels === 'string') { | |
labelsArray = results.labels.split(',').map(label => label.trim()); | |
} else if (Array.isArray(results.labels)) { | |
labelsArray = results.labels; // Use the original array if it's already split | |
} else { | |
console.warn("Unexpected labels format:", results.labels); | |
labelsArray = [results.labels.toString()]; // Fallback | |
} | |
// Use the first score (since the model might return one score for all labels) | |
const score = results.scores.length > 0 ? results.scores[0].toFixed(3) : '0.000'; | |
// Create a row for each label with the same score | |
if (labelsArray.length === 0) { | |
console.warn("No labels to display"); | |
return; | |
} | |
labelsArray.forEach((label) => { | |
const row = document.createElement('tr'); | |
row.innerHTML = `<td>${label}</td><td>${score}</td>`; | |
tbody.appendChild(row); | |
}); | |
} | |
function updateChart(results) { | |
const ctx = document.getElementById('resultsChart').getContext('2d'); | |
if (chartInstance) chartInstance.destroy(); | |
// If labels is a single string, split it manually for the chart | |
let labelsArray = []; | |
if (typeof results.labels === 'string') { | |
labelsArray = results.labels.split(',').map(label => label.trim()); | |
} else if (Array.isArray(results.labels)) { | |
labelsArray = results.labels; | |
} else { | |
labelsArray = [results.labels.toString()]; | |
} | |
// Create scores array with the same length as labelsArray | |
const score = results.scores.length > 0 ? results.scores[0] : 0; | |
const scoresArray = Array(labelsArray.length).fill(score); | |
chartInstance = new Chart(ctx, { | |
type: 'bar', | |
data: { | |
labels: labelsArray, | |
datasets: [{ | |
label: 'Scores', | |
data: scoresArray, | |
backgroundColor: '#4e79e6', | |
borderColor: '#6a8de9', | |
borderWidth: 1 | |
}] | |
}, | |
options: { | |
scales: { y: { beginAtZero: true, max: 1 } }, | |
plugins: { legend: { display: false } } | |
} | |
}); | |
} | |
</script> | |
</body> | |
</html> |