[FIX] ARIA chat: indicatore 'sta scrivendo' + input multi-riga (Shift+Invio)
Risolve #418/#420 (ARIA sembrava non rispondere / serviva riaprire il pannello): l'attesa della risposta /api/ai/ask (RAG+Claude, alcuni secondi) non mostrava alcun indicatore -> ora compare 'ARIA sta scrivendo...' con input disabilitato (anti doppio-invio). Indicatore rispetta prefers-reduced-motion (AGID/WCAG). Risolve #425: il campo di scrittura era <input> a riga singola -> ora <textarea> con Invio=invia, Shift+Invio=a capo, auto-crescita e aria-label. common.js e' statico (no PHP/USR2): bump cache-buster ?v=20260623 su 39 pagine (public + admin) + version.json 1.24.6. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4e2e56ec0b
commit
bd1e95542c
+52
-6
@@ -996,8 +996,24 @@ function switchLang(lang) {
|
||||
|
||||
var STORAGE_KEY = 'nis2_ai_chat';
|
||||
var history = [];
|
||||
var pending = false; // ARIA sta elaborando una risposta (mostra indicatore "sta scrivendo")
|
||||
try { history = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || '[]'); } catch (e) {}
|
||||
|
||||
// Stile una-tantum per l'indicatore "ARIA sta scrivendo…" (puntini animati,
|
||||
// rispetta prefers-reduced-motion per accessibilita AGID/WCAG).
|
||||
if (!document.getElementById('nis2-aria-style')) {
|
||||
var ariaStyle = document.createElement('style');
|
||||
ariaStyle.id = 'nis2-aria-style';
|
||||
ariaStyle.textContent =
|
||||
'@keyframes nis2AriaBlink{0%,80%,100%{opacity:.2}40%{opacity:1}}'
|
||||
+ '.nis2-aria-dot{display:inline-block;width:6px;height:6px;margin:0 1px;'
|
||||
+ 'border-radius:50%;background:#7C3AED;animation:nis2AriaBlink 1.2s infinite both}'
|
||||
+ '.nis2-aria-dot:nth-child(2){animation-delay:.2s}'
|
||||
+ '.nis2-aria-dot:nth-child(3){animation-delay:.4s}'
|
||||
+ '@media (prefers-reduced-motion:reduce){.nis2-aria-dot{animation:none;opacity:.6}}';
|
||||
document.head.appendChild(ariaStyle);
|
||||
}
|
||||
|
||||
function escHtml(s) {
|
||||
return String(s).replace(/[&<>"']/g, function (c) {
|
||||
return ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c];
|
||||
@@ -1043,31 +1059,60 @@ function switchLang(lang) {
|
||||
+ (list || '<div style="color:#9ca3af;font-size:13px;text-align:center;'
|
||||
+ 'margin-top:24px">Ciao! Chiedimi qualcosa su NIS2: misure di '
|
||||
+ 'sicurezza, audit, fornitori, incident response…</div>')
|
||||
+ (pending
|
||||
? '<div id="ai-chat-typing" aria-live="polite" '
|
||||
+ 'style="margin-bottom:10px;display:flex;justify-content:flex-start">'
|
||||
+ '<div style="padding:8px 12px;border-radius:14px;font-size:13px;'
|
||||
+ 'background:#f3f4f6;color:#6b7280">ARIA sta scrivendo '
|
||||
+ '<span class="nis2-aria-dot"></span><span class="nis2-aria-dot"></span>'
|
||||
+ '<span class="nis2-aria-dot"></span></div></div>'
|
||||
: '')
|
||||
+ '</div>'
|
||||
+ '<form id="ai-chat-form" style="display:flex;gap:6px;padding:10px;'
|
||||
+ 'border-top:1px solid #e5e7eb;background:#fff">'
|
||||
+ '<input id="ai-chat-input" type="text" placeholder="Scrivi…" '
|
||||
+ 'autocomplete="off" style="flex:1;padding:10px 12px;border:1px solid '
|
||||
+ '#d1d5db;border-radius:10px;font-size:14px;font-family:inherit">'
|
||||
+ '<button type="submit" style="padding:10px 14px;border:none;'
|
||||
+ 'border-top:1px solid #e5e7eb;background:#fff;align-items:flex-end">'
|
||||
+ '<textarea id="ai-chat-input" rows="1" '
|
||||
+ 'placeholder="Scrivi… (Invio per inviare, Shift+Invio per andare a capo)" '
|
||||
+ 'aria-label="Scrivi un messaggio ad ARIA" autocomplete="off"'
|
||||
+ (pending ? ' disabled' : '')
|
||||
+ ' style="flex:1;padding:10px 12px;border:1px solid #d1d5db;border-radius:10px;'
|
||||
+ 'font-size:14px;font-family:inherit;resize:none;max-height:120px;'
|
||||
+ 'overflow-y:auto;line-height:1.4"></textarea>'
|
||||
+ '<button type="submit"' + (pending ? ' disabled' : '')
|
||||
+ ' style="padding:10px 14px;border:none;'
|
||||
+ 'border-radius:10px;background:linear-gradient(135deg,#7C3AED,#3B82F6);'
|
||||
+ 'color:#fff;font-weight:600;cursor:pointer">'
|
||||
+ 'color:#fff;font-weight:600;cursor:' + (pending ? 'not-allowed' : 'pointer')
|
||||
+ ';opacity:' + (pending ? '.6' : '1') + '">'
|
||||
+ '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>'
|
||||
+ '</button>'
|
||||
+ '</form>';
|
||||
document.getElementById('ai-chat-close').onclick = closePanel;
|
||||
document.getElementById('ai-chat-form').onsubmit = onSubmit;
|
||||
var inputEl = document.getElementById('ai-chat-input');
|
||||
if (inputEl) {
|
||||
// Invio = invia · Shift+Invio = vai a capo (textarea multi-riga)
|
||||
inputEl.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); onSubmit(e); }
|
||||
});
|
||||
// Auto-crescita altezza della textarea fino a max-height
|
||||
inputEl.addEventListener('input', function () {
|
||||
inputEl.style.height = 'auto';
|
||||
inputEl.style.height = Math.min(inputEl.scrollHeight, 120) + 'px';
|
||||
});
|
||||
if (!pending) inputEl.focus();
|
||||
}
|
||||
var msgs = document.getElementById('ai-chat-msgs');
|
||||
msgs.scrollTop = msgs.scrollHeight;
|
||||
}
|
||||
|
||||
async function onSubmit(ev) {
|
||||
ev.preventDefault();
|
||||
if (pending) return; // evita invii doppi mentre ARIA elabora
|
||||
var input = document.getElementById('ai-chat-input');
|
||||
var text = (input.value || '').trim();
|
||||
if (!text) return;
|
||||
history.push({ role: 'user', content: text });
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(history));
|
||||
pending = true;
|
||||
render();
|
||||
try {
|
||||
// AI nativa NIS2 (RAG con KB Multi-Livello già attivo)
|
||||
@@ -1094,6 +1139,7 @@ function switchLang(lang) {
|
||||
content: '⚠️ ARIA non risponde in questo momento. Riprova tra poco.'
|
||||
});
|
||||
}
|
||||
pending = false;
|
||||
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(history));
|
||||
render();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user