rustemgareev's picture
Upload app files
eb59cf9
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>mdeberta-ner-ontonotes5</title>
<meta name="description" content="Named Entity Recognition Demo">
<style>
:root {
--c-misc: #f3e5f5;
--t-misc: #4a148c;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 40px 20px;
background-color: #fafafa;
color: #111;
line-height: 1.6;
}
.container {
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.04);
border: 1px solid #eaeaea;
}
header {
margin-bottom: 30px;
}
h1 {
font-size: 24px;
font-weight: 600;
margin: 0 0 8px 0;
letter-spacing: -0.02em;
}
.subtitle {
color: #666;
font-size: 14px;
}
/* Input Section */
.input-group {
margin-bottom: 20px;
position: relative;
}
label {
display: block;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
color: #888;
margin-bottom: 8px;
letter-spacing: 0.05em;
}
textarea {
width: 100%;
min-height: 120px;
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
font-size: 16px;
line-height: 1.6;
resize: vertical;
background-color: #fff;
box-sizing: border-box;
font-family: inherit;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
color: #333;
}
textarea:focus {
border-color: #000;
box-shadow: 0 0 0 2px rgba(0,0,0,0.05);
}
/* Button */
button.main-btn {
background-color: #1a1a1a;
color: white;
border: none;
padding: 14px 32px;
border-radius: 8px;
font-family: inherit;
font-size: 15px;
font-weight: 500;
cursor: pointer;
display: block;
width: 100%;
transition: background-color 0.2s, transform 0.1s;
}
button.main-btn:hover {
background-color: #333;
}
button.main-btn:active {
transform: scale(0.99);
}
button.main-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
transform: none;
}
/* Output Section */
#output-wrapper {
margin-top: 30px;
display: none;
animation: fadeIn 0.3s ease-out;
}
.result-box {
padding: 20px;
border: 1px solid #eee;
background-color: #fcfcfc;
border-radius: 8px;
font-size: 16px;
line-height: 1.8;
white-space: pre-wrap;
}
/* Entity Styling */
.entity {
padding: 2px 6px;
border-radius: 4px;
font-weight: 500;
cursor: help;
position: relative;
transition: background-color 0.2s;
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
/* Tooltip */
.entity::after {
content: attr(data-label) " " attr(data-score);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%) translateY(-4px);
background: #1a1a1a;
color: #fff;
padding: 4px 8px;
border-radius: 4px;
font-size: 11px;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s, transform 0.2s;
z-index: 10;
font-weight: 400;
}
.entity:hover::after {
opacity: 1;
transform: translateX(-50%) translateY(-8px);
}
.type-DEFAULT { background: var(--c-misc); color: var(--t-misc); }
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.error-msg {
color: #d32f2f;
background: #ffebee;
padding: 12px;
border-radius: 6px;
margin-top: 20px;
font-size: 14px;
display: none;
}
@media (max-width: 600px) {
body { padding: 20px 10px; }
.container { padding: 24px; }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>mdeberta-ner-ontonotes5</h1>
<div class="subtitle">Named Entity Recognition Demo</div>
</header>
<div class="input-group">
<label for="inputText">Input Text</label>
<textarea id="inputText" placeholder="Enter text to analyze...">Apple Inc. is looking at buying a U.K. startup for $1 billion in London next week.</textarea>
</div>
<button id="analyzeBtn" class="main-btn" onclick="analyze()">Analyze Text</button>
<div id="errorBox" class="error-msg"></div>
<div id="output-wrapper">
<label>Result</label>
<div id="resultBox" class="result-box"></div>
</div>
</div>
<script>
async function analyze() {
const input = document.getElementById('inputText');
const btn = document.getElementById('analyzeBtn');
const outputWrapper = document.getElementById('output-wrapper');
const resultBox = document.getElementById('resultBox');
const errorBox = document.getElementById('errorBox');
const text = input.value.trim();
if (!text) return;
// Reset UI
btn.disabled = true;
btn.textContent = "Processing...";
errorBox.style.display = 'none';
outputWrapper.style.display = 'none';
try {
const response = await fetch('/predict', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text })
});
if (!response.ok) {
throw new Error(`Server Error: ${response.statusText}`);
}
const data = await response.json();
renderResult(text, data.entities);
// Show result
outputWrapper.style.display = 'block';
} catch (err) {
errorBox.textContent = err.message;
errorBox.style.display = 'block';
} finally {
btn.disabled = false;
btn.textContent = "Analyze Text";
}
}
function renderResult(originalText, entities) {
const resultBox = document.getElementById('resultBox');
resultBox.innerHTML = '';
if (!entities || entities.length === 0) {
resultBox.textContent = originalText;
return;
}
let lastIndex = 0;
entities.forEach(entity => {
// 1. Text before entity
const plainText = originalText.slice(lastIndex, entity.start);
resultBox.appendChild(document.createTextNode(plainText));
// 2. Entity
const span = document.createElement('span');
// Determine class based on entity group (fallback to DEFAULT)
const type = entity.entity_group || 'DEFAULT';
// Check if specific class exists in CSS is hard in JS, so we rely on CSS fallbacks or generic logic
// Here we map common groups to classes, others fall to DEFAULT via CSS if not defined
span.className = `entity type-${type} type-DEFAULT`;
span.textContent = originalText.slice(entity.start, entity.end);
// Tooltip data
span.setAttribute('data-label', type);
span.setAttribute('data-score', Math.round(entity.score * 100) + '%');
resultBox.appendChild(span);
lastIndex = entity.end;
});
// 3. Remaining text
resultBox.appendChild(document.createTextNode(originalText.slice(lastIndex)));
}
</script>
</body>
</html>