devusman's picture
all code
9c9f9cc
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Analisi Logica Avanzata</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--bg-secondary: #ffffff;
--bg-tertiary: #f8fafc;
--text-primary: #2d3748;
--text-secondary: #4a5568;
--border-color: #e2e8f0;
--shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
--shadow-hover: 0 15px 35px rgba(0, 0, 0, 0.15);
--subject-color: #c53030;
--predicate-color: #2c7a7b;
--object-color: #2b6cb0;
--complement-color: #2f855a;
--modifier-color: #b7791f;
--clause-color: #553c9a;
--title-color: #ffffff;
--accent-color: #667eea;
--success-color: #38a169;
--error-color: #e53e3e;
--info-color: #3182ce;
--entity-color: #dd6b20;
}
[data-theme="scuro"] {
--bg-primary: linear-gradient(135deg, #2d3748 0%, #1a202c 100%);
--bg-secondary: #2d3748;
--bg-tertiary: #1a202c;
--text-primary: #f7fafc;
--text-secondary: #a0aec0;
--border-color: #4a5568;
--title-color: #ffffff;
--accent-color: #667eea;
--success-color: #68d391;
--error-color: #fc8181;
--info-color: #63b3ed;
--entity-color: #f6ad55;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.container {
max-width: 1000px;
margin: 0 auto;
background: var(--bg-secondary);
border-radius: 20px;
box-shadow: var(--shadow);
overflow: hidden;
transition: all 0.3s ease;
}
.header {
background: var(--bg-primary);
color: var(--title-color);
padding: 40px 30px;
text-align: center;
position: relative;
}
.theme-toggle {
position: absolute;
top: 20px;
right: 20px;
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
padding: 10px;
width: 44px;
height: 44px;
border-radius: 50%;
cursor: pointer;
font-size: 20px;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
}
.theme-toggle:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.1) rotate(15deg);
}
.header h1 {
font-size: clamp(2rem, 5vw, 3.2rem);
margin-bottom: 10px;
text-shadow: 2px 2px 8px rgba(0, 0, 0, 0.4);
font-family: 'Trebuchet MS', 'Arial Black', sans-serif;
font-weight: 900;
}
.header p {
font-size: clamp(1rem, 2.5vw, 1.25rem);
opacity: 0.95;
}
.main-content {
padding: 40px;
background: var(--bg-secondary);
transition: all 0.3s ease;
}
.input-section {
margin-bottom: 30px;
}
.input-container {
position: relative;
margin-bottom: 20px;
}
#sentenceInput {
width: 100%;
padding: 20px;
padding-right: 60px;
border: 2px solid var(--border-color);
border-radius: 15px;
font-size: 18px;
background: var(--bg-tertiary);
color: var(--text-primary);
transition: all 0.3s ease;
resize: vertical;
min-height: 120px;
font-family: inherit;
}
#sentenceInput:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.2);
}
#sentenceInput::placeholder {
color: var(--text-secondary);
opacity: 0.7;
}
.voice-btn {
position: absolute;
right: 15px;
top: 15px;
background: transparent;
border: none;
cursor: pointer;
color: var(--text-secondary);
padding: 10px;
border-radius: 50%;
transition: all 0.3s ease;
}
.voice-btn:hover {
background-color: rgba(102, 126, 234, 0.1);
color: var(--accent-color);
}
.voice-btn.is-listening {
color: var(--error-color);
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(229, 62, 62, 0.7);
}
70% {
transform: scale(1.1);
box-shadow: 0 0 10px 10px rgba(229, 62, 62, 0);
}
100% {
transform: scale(1);
box-shadow: 0 0 0 0 rgba(229, 62, 62, 0);
}
}
.analyze-btn {
background: var(--bg-primary);
color: #ffffff;
border: none;
padding: 15px 40px;
border-radius: 50px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: var(--shadow);
display: block;
margin: 0 auto;
}
.analyze-btn:hover {
box-shadow: var(--shadow-hover);
transform: translateY(-2px);
}
.analyze-btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.examples {
margin: 30px 0;
text-align: center;
}
.examples h3 {
color: var(--text-secondary);
margin-bottom: 15px;
font-size: 1rem;
font-weight: 500;
}
.example-btn {
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
color: var(--text-secondary);
padding: 8px 15px;
margin: 4px;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
}
.example-btn:hover {
background: rgba(102, 126, 234, 0.1);
border-color: var(--accent-color);
color: var(--accent-color);
}
.results {
margin-top: 30px;
display: none;
}
.format-selector {
display: flex;
justify-content: center;
gap: 10px;
margin: 20px 0 30px 0;
}
.format-btn {
background: var(--bg-tertiary);
border: 2px solid var(--border-color);
color: var(--text-secondary);
padding: 10px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s ease;
font-weight: 600;
}
.format-btn.active {
background: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
.result-item {
border: 1px solid var(--border-color);
border-radius: 15px;
padding: 25px;
margin-bottom: 25px;
transition: all 0.3s ease;
overflow: hidden;
background: var(--bg-tertiary);
}
.subordinate-clause {
margin-left: 40px;
border-left: 3px solid var(--clause-color);
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 10px;
}
.result-title {
font-weight: 700;
font-size: 1.3rem;
color: var(--text-primary);
}
.result-subtitle {
font-weight: 500;
font-size: 1rem;
color: var(--clause-color);
}
.copy-btn {
background: rgba(102, 126, 234, 0.1);
border: none;
color: var(--accent-color);
padding: 8px 15px;
border-radius: 20px;
cursor: pointer;
font-size: 12px;
font-weight: 600;
transition: all 0.3s ease;
flex-shrink: 0;
}
.copy-btn:hover {
background: rgba(102, 126, 234, 0.2);
}
.component {
display: inline-block;
padding: 6px 12px;
margin: 4px 2px;
border-radius: 8px;
font-weight: 500;
color: white;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
cursor: help;
}
.subject {
background: var(--subject-color);
}
.predicate {
background: var(--predicate-color);
}
.object {
background: var(--object-color);
}
.complement {
background: var(--complement-color);
}
.modifier {
background: var(--modifier-color);
}
.clause {
background: var(--clause-color);
}
.entity {
background: var(--entity-color);
}
.table-format {
width: 100%;
border-collapse: collapse;
margin-top: 15px;
background: var(--bg-secondary);
}
.table-format th,
.table-format td {
padding: 12px 15px;
border-bottom: 1px solid var(--border-color);
text-align: left;
vertical-align: top;
}
.table-format th {
background: rgba(102, 126, 234, 0.05);
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-secondary);
}
.table-format td {
color: var(--text-primary);
}
.table-format td:nth-child(2) {
font-weight: 600;
}
.table-format td small {
display: block;
margin-top: 5px;
color: var(--text-secondary);
font-size: 0.85rem;
line-height: 1.4;
}
/* Entity Section */
.entities-container {
margin-top: 20px;
}
.entity-tag {
display: inline-block;
padding: 8px 12px;
margin: 5px;
border-radius: 8px;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
transition: all 0.3s ease;
}
.entity-text {
font-weight: 600;
color: var(--entity-color);
}
.entity-label {
font-size: 0.8rem;
background: var(--border-color);
color: var(--text-secondary);
padding: 2px 6px;
border-radius: 4px;
margin-left: 8px;
font-weight: 600;
}
.entity-explanation {
display: block;
font-size: 0.9rem;
margin-top: 5px;
color: var(--text-secondary);
}
/* Updated Loading Styles - Bottom loader only */
.loading-section {
margin-top: 30px;
padding: 40px 20px;
background: var(--bg-tertiary);
border-radius: 15px;
border: 1px solid var(--border-color);
text-align: center;
display: none;
}
.loading-section.show {
display: block;
}
.spinner {
border: 4px solid var(--border-color);
border-top: 4px solid var(--accent-color);
border-radius: 50%;
width: 50px;
height: 50px;
animation: spin 1s linear infinite;
margin: 0 auto 20px auto;
}
.loading-text {
color: var(--text-primary);
font-size: 1.1rem;
font-weight: 500;
margin-bottom: 10px;
}
.loading-subtext {
color: var(--text-secondary);
font-size: 0.95rem;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.toast {
position: fixed;
top: 20px;
right: 20px;
color: white;
padding: 12px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
z-index: 1000;
font-weight: 600;
transform: translateX(calc(100% + 20px));
transition: transform 0.4s ease-in-out;
}
.toast.show {
transform: translateX(0);
}
.toast.successo {
background: var(--success-color);
}
.toast.errore {
background: var(--error-color);
}
.toast.info {
background: var(--info-color);
}
@media (max-width: 600px) {
body {
padding: 10px;
}
.container {
border-radius: 15px;
}
.main-content {
padding: 20px;
}
.subordinate-clause {
margin-left: 15px;
}
#sentenceInput {
min-height: 100px;
padding: 15px;
padding-right: 50px;
}
.example-btn {
font-size: 13px;
padding: 6px 10px;
}
.format-selector {
flex-direction: column;
align-items: center;
gap: 8px;
}
.format-btn {
width: 150px;
}
.table-format {
font-size: 0.9rem;
}
.table-format th,
.table-format td {
padding: 8px;
}
.loading-section {
padding: 30px 15px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<button class="theme-toggle"></button>
<h1>Analisi Logica Avanzata</h1>
<p>Scomponi qualsiasi frase con la potenza dell'IA</p>
</div>
<div class="main-content">
<div class="input-section">
<div class="input-container">
<textarea id="sentenceInput"
placeholder="Es: Il ragazzo, che ha studiato molto, ha superato l'esame con un ottimo voto."
rows="4"></textarea>
<button id="voiceInputBtn" class="voice-btn" title="Avvia riconoscimento vocale">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" width="24"
height="24">
<path
d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.49 6-3.31 6-6.72h-1.7z" />
</svg>
</button>
</div>
<button class="analyze-btn" onclick="analizzaFrase()">Analizza Frase</button>
</div>
<div class="examples">
<h3>Oppure prova con un esempio:</h3>
<button class="example-btn"
onclick="impostaEsempio('Il cane corre velocemente nel parco.')">Semplice</button>
<button class="example-btn"
onclick="impostaEsempio('Maria ha regalato a suo fratello un libro che era molto interessante.')">Con
Subordinata</button>
<button class="example-btn"
onclick="impostaEsempio('Mentre il sole tramontava, la nave salpava dal porto di Genova.')">Complessa</button>
<button class="example-btn"
onclick="impostaEsempio('Il Colosseo fu costruito dagli imperatori Flavi per intrattenere il popolo.')">Passiva</button>
</div>
<!-- Loading section at the bottom -->
<div id="loadingSection" class="loading-section">
<div class="spinner"></div>
<div class="loading-text">Analisi in corso...</div>
<div class="loading-subtext">Stiamo processando la tua frase con intelligenza artificiale</div>
</div>
<div id="results" class="results"></div>
</div>
</div>
<script>
let formatoCorrente = 'visuale';
const API_URL = 'https://devusman-analysis-tool.hf.space/api/analyze';
const etichettaAClasseColore = {
'Soggetto': 'subject',
'Predicato Verbale': 'predicate',
'Parte Nominale del Predicato': 'predicate',
'Copula': 'predicate',
'Complemento Oggetto': 'object',
'Complemento di Termine': 'complement',
'Complemento di Specificazione': 'complement',
'Complemento d\'Agente': 'complement',
'Complemento di Moto da Luogo': 'complement',
'Complemento di Stato in Luogo': 'complement',
'Complemento di Compagnia o Mezzo': 'complement',
'Complemento di Argomento o Luogo': 'complement',
'Complemento di Fine o Causa': 'complement',
'Complemento di Luogo o Tempo (Partitivo)': 'complement',
'Complemento Indiretto': 'complement',
'Attributo': 'modifier',
'Apposizione': 'modifier',
'Complemento Avverbiale': 'modifier',
'Proposizione Subordinata Relativa': 'clause',
'Proposizione Subordinata Avverbiale': 'clause',
'Proposizione Subordinata Oggettiva': 'clause',
'Proposizione Subordinata Soggettiva': 'clause'
};
document.addEventListener('DOMContentLoaded', () => {
const cambiaTemaBtn = document.querySelector('.theme-toggle');
const temaSalvato = localStorage.getItem('tema') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'scuro' : 'chiaro');
document.documentElement.setAttribute('data-theme', temaSalvato);
cambiaTemaBtn.textContent = temaSalvato === 'scuro' ? '☀️' : '☾';
cambiaTemaBtn.addEventListener('click', cambiaTema);
impostaRiconoscimentoVocale();
document.getElementById('sentenceInput').addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
analizzaFrase();
}
});
});
function cambiaTema() {
const temaCorrente = document.documentElement.getAttribute('data-theme');
const nuovoTema = temaCorrente === 'scuro' ? 'chiaro' : 'scuro';
document.documentElement.setAttribute('data-theme', nuovoTema);
localStorage.setItem('tema', nuovoTema);
document.querySelector('.theme-toggle').textContent = nuovoTema === 'scuro' ? '☀️' : '☾';
}
function impostaEsempio(frase) {
document.getElementById('sentenceInput').value = frase;
analizzaFrase();
}
function impostaFormato(formato) {
formatoCorrente = formato;
const risultatiDiv = document.getElementById('results');
if (risultatiDiv.dataset.analysis) {
const analisi = JSON.parse(risultatiDiv.dataset.analysis);
mostraRisultati(analisi);
}
}
async function analizzaFrase() {
const frase = document.getElementById('sentenceInput').value.trim();
const risultatiDiv = document.getElementById('results');
const sezioneCaricamento = document.getElementById('loadingSection');
const analizzaBtn = document.querySelector('.analyze-btn');
if (!frase) {
mostraNotifica('Per favore, inserisci una frase.', 'info');
return;
}
sezioneCaricamento.classList.add('show');
risultatiDiv.style.display = 'none';
analizzaBtn.disabled = true;
analizzaBtn.textContent = 'Analizzando...';
risultatiDiv.dataset.analysis = "";
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ sentence: frase }),
});
if (!response.ok) {
const datiErrore = await response.json().catch(() => ({}));
throw new Error(datiErrore.error || `Errore del server: ${response.status}`);
}
const analisi = await response.json();
risultatiDiv.dataset.analysis = JSON.stringify(analisi);
sezioneCaricamento.classList.remove('show');
mostraRisultati(analisi);
mostraNotifica('Analisi completata!', 'successo');
} catch (errore) {
console.error('Errore di analisi:', errore);
sezioneCaricamento.classList.remove('show');
risultatiDiv.innerHTML = `
<div class="result-item" style="text-align: center; color: var(--error-color); border-color: var(--error-color);">
<h3 style="margin-bottom: 10px;">Errore nell'analisi</h3>
<p>${errore.message}</p>
</div>`;
risultatiDiv.style.display = 'block';
mostraNotifica('Analisi fallita. Riprova.', 'errore');
} finally {
analizzaBtn.disabled = false;
analizzaBtn.textContent = 'Analizza Frase';
}
}
function mostraRisultati(analisi) {
const risultatiDiv = document.getElementById('results');
let html = `
<h2 style="color: var(--text-primary); margin-bottom: 20px; text-align: center; font-size: 1.8rem;">Risultati dell'Analisi</h2>
<div class="format-selector">
<button class="format-btn ${formatoCorrente === 'visuale' ? 'active' : ''}" onclick="impostaFormato('visuale')">📊 Visuale</button>
<button class="format-btn ${formatoCorrente === 'tabella' ? 'active' : ''}" onclick="impostaFormato('tabella')">📋 Tabella</button>
</div>`;
if (analisi.main_clause && analisi.main_clause.analysis.length > 0) {
html += generaHTMLClausola(analisi.main_clause, 'Proposizione Principale', false);
}
if (analisi.subordinate_clauses && analisi.subordinate_clauses.length > 0) {
analisi.subordinate_clauses.forEach(subClausola => {
const titolo = subClausola.type_info ? subClausola.type_info.label : 'Proposizione Subordinata';
html += generaHTMLClausola(subClausola, titolo, true, subClausola.intro);
});
}
if (analisi.named_entities && analisi.named_entities.length > 0) {
html += generaHTMLEntita(analisi.named_entities);
}
risultatiDiv.innerHTML = html;
risultatiDiv.style.display = 'block';
}
function generaHTMLClausola(datiClausola, titolo, isSubordinata = false, intro = '') {
const classeClausola = isSubordinata ? 'result-item subordinate-clause' : 'result-item';
const testoAnalisiPerCopia = datiClausola.analysis.map(item =>
`${item.text} (${item.label_info.label}: ${item.label_info.description})`
).join('\n');
let html = `<div class="${classeClausola}">
<div class="result-header">
<div>
<div class="result-title">${titolo}</div>
${isSubordinata && intro ? `<div class="result-subtitle">Introdotta da: <strong>${intro}</strong></div>` : ''}
</div>
<button class="copy-btn" onclick="copiaNegliAppunti(\`${testoAnalisiPerCopia.replace(/`/g, '\\`')}\`)">📋 Copia Analisi</button>
</div>`;
if (formatoCorrente === 'visuale') {
const scomposizioneVisuale = datiClausola.analysis.map(item => {
const classeColore = etichettaAClasseColore[item.label_info.label] || 'complement';
// MODIFICA 1: Rimuove i dettagli grammaticali dal tooltip
const tooltip = `FUNZIONE: ${item.label_info.label}\nDESCRIZIONE: ${item.label_info.description}`;
return `<span class="component ${classeColore}" title="${tooltip}">${item.text}</span>`;
}).join(' ');
html += `<div style="font-size: 1.1rem; line-height: 2.2; margin-top: 15px;">${scomposizioneVisuale}</div>`;
} else { // formato tabella
// MODIFICA 2: Rimuove l'intestazione della colonna "Dettagli Grammaticali"
html += `<table class="table-format">
<thead><tr><th>Elemento</th><th>Funzione Logica</th></tr></thead>
<tbody>`;
datiClausola.analysis.forEach(item => {
const classeColore = etichettaAClasseColore[item.label_info.label] || 'complement';
// MODIFICA 3: Rimuove la cella della tabella con i dettagli
html += `<tr>
<td><span class="component ${classeColore}">${item.text}</span></td>
<td>
${item.label_info.label}
<small>${item.label_info.description}</small>
</td>
</tr>`;
});
html += `</tbody></table>`;
}
html += `</div>`;
return html;
}
function generaHTMLEntita(entita) {
let html = `
<div class="result-item entities-container">
<div class="result-header"><div class="result-title">Entità Riconosciute</div></div>
`;
entita.forEach(ent => {
html += `
<div class="entity-tag">
<span class="entity-text">${ent.text}</span>
<span class="entity-label">${ent.label}</span>
<span class="entity-explanation">${ent.explanation}</span>
</div>
`;
});
html += `</div>`;
return html;
}
function impostaRiconoscimentoVocale() {
const voiceBtn = document.getElementById('voiceInputBtn');
const sentenceInput = document.getElementById('sentenceInput');
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
voiceBtn.style.display = 'none';
return;
}
const recognition = new SpeechRecognition();
recognition.lang = 'it-IT';
recognition.interimResults = false;
recognition.maxAlternatives = 1;
voiceBtn.addEventListener('click', () => {
if (voiceBtn.classList.contains('is-listening')) {
recognition.stop();
} else {
recognition.start();
}
});
recognition.onstart = () => {
voiceBtn.classList.add('is-listening');
mostraNotifica('Ascolto in corso...', 'info');
};
recognition.onend = () => voiceBtn.classList.remove('is-listening');
recognition.onerror = (event) => {
mostraNotifica(`Errore: ${event.error}`, 'errore');
voiceBtn.classList.remove('is-listening');
};
recognition.onresult = (event) => {
sentenceInput.value = event.results[0][0].transcript;
mostraNotifica('Frase trascritta! Clicca per analizzare.', 'successo');
};
}
function copiaNegliAppunti(testo) {
navigator.clipboard.writeText(testo).then(() => {
mostraNotifica('Analisi copiata negli appunti!', 'successo');
}, (err) => {
mostraNotifica('Errore nella copia', 'errore');
console.error('Copia fallita:', err);
});
}
function mostraNotifica(messaggio, tipo = 'successo') {
document.querySelectorAll('.toast').forEach(t => t.remove());
const notifica = document.createElement('div');
notifica.className = `toast ${tipo}`;
notifica.textContent = messaggio;
document.body.appendChild(notifica);
setTimeout(() => notifica.classList.add('show'), 10);
setTimeout(() => {
notifica.classList.remove('show');
setTimeout(() => notifica.remove(), 400);
}, 3000);
}
</script>
</body>
</html>