MediaWiki:CommonDashboard.js: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 1: | Riga 1: | ||
/* ===================== MPAI – CHAT APP (no inline script) ===================== */ | /* ===================== MPAI – CHAT APP (no inline script) ===================== */ | ||
/ | /** Guardia anti-doppio-caricamento (strumentata) */ | ||
if (window.__MP_DASHBOARD_LOADED__) { | if (window.__MP_DASHBOARD_LOADED__) { | ||
const src = (document.currentScript && document.currentScript.src) || '(inline)'; | const src = (document.currentScript && document.currentScript.src) || '(inline)'; | ||
| Riga 17: | Riga 15: | ||
window.__MP_DASHBOARD_LOADED__ = true; | window.__MP_DASHBOARD_LOADED__ = true; | ||
/** Se qualche script ha rotto $, lo ri-aggancio a jQuery */ | |||
if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) { | if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) { | ||
window.$ = window.jQuery; | window.$ = window.jQuery; | ||
} | } | ||
// | /** Shortcut locale */ | ||
window.$mp = window.$mp || (s => document.querySelector(s)); | window.$mp = window.$mp || (s => document.querySelector(s)); | ||
(function () { | (function () { | ||
console.log('🧭 MPAI: init…'); | console.log('🧭 MPAI: init…'); | ||
/ | /* ===================== STATO ===================== */ | ||
const mpaiState = { | |||
const | currentProject: null, // nome progetto (se selezionato) | ||
sessionId: null, // id bozza locale | |||
sessionMeta: null, // {title, updated} | |||
history: [], | |||
attachments: [] | |||
}; | |||
/* =========================================== | /* ===================== UI HELPERS ===================== */ | ||
function mpaiAppendMsg(role, content) { | |||
=================== | const chatEl = $mp('#mpai-chat'); | ||
if (!chatEl) return; | |||
const div = document.createElement('div'); | |||
div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error')); | |||
div.innerHTML = ` | |||
<div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div> | |||
<div class="content" style="white-space:pre-wrap">${content}</div>`; | |||
chatEl.appendChild(div); | |||
chatEl.scrollTop = chatEl.scrollHeight; | |||
} | |||
function mpaiRenderFiles() { | |||
const fileListEl = $mp('#mpai-file-list'); | |||
if (!fileListEl) return; | |||
fileListEl.innerHTML = ''; | |||
mpaiState.attachments.forEach((a, i) => { | |||
const pill = document.createElement('span'); | |||
pill.className = 'mpai-filepill'; | |||
pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`; | |||
pill.querySelector('button').onclick = () => { mpaiState.attachments.splice(i, 1); mpaiRenderFiles(); }; | |||
fileListEl.appendChild(pill); | |||
}); | |||
} | |||
/* ===================== PERSISTENZA ===================== */ | |||
function mpaiSaveLocal() { | |||
if (!mpaiState.currentProject) return; | |||
localStorage.setItem('mpai.hist.' + mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200))); | |||
} | |||
function mpaiLoadLocal(project) { | |||
const chatEl = $mp('#mpai-chat'); | |||
if (!chatEl) return; | |||
const raw = localStorage.getItem('mpai.hist.' + project); | |||
mpaiState.history = raw ? JSON.parse(raw) : []; | |||
chatEl.innerHTML = ''; | |||
if (!mpaiState.history.length) { | |||
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "' + project + '". Allegami anche file sanificati se servono.'); | |||
} else { | |||
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content)); | |||
} | |||
} | |||
function mpaiEnsureSession() { | |||
if (!mpaiState.sessionId) { | |||
mpaiState.sessionId = 'sess-' + Date.now(); | |||
mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() }; | |||
localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta)); | |||
} | |||
} | } | ||
function mpaiSaveSession() { | |||
if (!mpaiState.sessionId) return; | |||
const key = 'mpai.session.hist.' + mpaiState.sessionId; | |||
function | localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200))); | ||
mpaiState.sessionMeta.updated = Date.now(); | |||
localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta)); | |||
mpaiState.sessionMeta = | |||
localStorage.setItem('mpai.session.meta.'+mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta)); | |||
} | } | ||
function mpaiLoadSession(id) { | |||
const chatEl = $mp('#mpai-chat'); | |||
if (!chatEl) return; | |||
mpaiState.sessionId = id; | |||
const key = 'mpai.session.hist.' + id; | |||
const raw = localStorage.getItem(key); | |||
mpaiState.history = raw ? JSON.parse(raw) : []; | |||
const metaRaw = localStorage.getItem('mpai.session.meta.' + id); | |||
function mpaiLoadSession(id){ | mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : { title: 'Nuova conversazione', updated: Date.now() }; | ||
chatEl.innerHTML = ''; | |||
if (!mpaiState.history.length) { | |||
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.'); | |||
} else { | |||
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content)); | |||
} | |||
} | } | ||
/* | /* ===================== PROGETTI (sidebar) ===================== */ | ||
async function mpaiLoadProjects(){ | async function mpaiLoadProjects() { | ||
const box = $mp('#mpai-project-list'); | |||
if (!box) return; | |||
box.innerHTML = '<em class="muted">Carico progetti…</em>'; | |||
try { | |||
const r = await fetch('/dashboard/api/project_list.php', { cache: 'no-store', credentials: 'include' }); | |||
const j = await r.json(); | |||
const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []); | |||
if (!arr.length) { box.innerHTML = '<em class="muted">Nessun progetto</em>'; return; } | |||
box.innerHTML = ''; | |||
arr.forEach(item => { | |||
const name = (typeof item === 'string') ? item : (item.name || item); | |||
const row = document.createElement('div'); | |||
row.className = 'row'; | |||
row.innerHTML = `<span class="title">${name}</span> | |||
<span><button class="mpai-btn" data-open="${name}">Apri</button></span>`; | |||
row.querySelector('[data-open]').onclick = () => { | |||
mpaiState.currentProject = name; | |||
mpaiLoadLocal(name); | |||
}; | |||
box.appendChild(row); | |||
}); | |||
if (!mpaiState.currentProject) { | |||
const first = (typeof arr[0] === 'string') ? arr[0] : (arr[0].name || arr[0]); | |||
mpaiState.currentProject = first; | |||
mpaiLoadLocal(first); | |||
} | |||
} catch (e) { | |||
box.innerHTML = '<span class="muted">Errore caricamento: ' + e.message + '</span>'; | |||
} | } | ||
} | } | ||
async function mpaiCreateProject() { | |||
const inp = $mp('#mpai-project-name'); | |||
const name = (inp?.value || '').trim(); | |||
if (!name) { alert('Inserisci un nome progetto'); return; } | |||
try { | |||
const r = await fetch('/dashboard/api/project_create.php', { | |||
method: 'POST', | |||
headers: { 'Content-Type': 'application/json' }, | |||
credentials: 'include', | |||
body: JSON.stringify({ name }) | |||
}); | |||
const txt = await r.text(); let j; try { j = JSON.parse(txt) } catch { j = { ok: false, raw: txt }; } | |||
if (j.ok !== false) { await mpaiLoadProjects(); mpaiState.currentProject = name; mpaiState.history = []; mpaiLoadLocal(name); inp.value = ''; } | |||
else { alert('Errore creazione: ' + (j.error || '')); } | |||
} catch (e) { alert('Errore rete: ' + e.message); } | |||
} | |||
/* ===================== INVIO PROMPT → PROXY ===================== */ | |||
window.sendPrompt = async function () { | |||
try { | |||
const ta = document.querySelector('#mpai-input'); | |||
if (!ta) return; | |||
const content = (ta.value || '').trim(); | |||
if (!content) { ta.focus(); return; } | |||
// mostra subito il messaggio utente | |||
mpaiState.history.push({ role: 'user', content }); | |||
mpaiAppendMsg('user', content); | |||
ta.value = ''; | |||
const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim(); | |||
// placeholder "Elaboro…" | |||
const pending = document.createElement('div'); | |||
pending.className = 'mpai-msg'; | |||
pending.innerHTML = '<em class="muted">Elaboro…</em>'; | |||
document.querySelector('#mpai-chat')?.appendChild(pending); | |||
const r = await fetch('/dashboard/api/openai_project_gpt.php', { | |||
method: 'POST', | |||
headers: { 'Content-Type': 'application/json' }, | |||
credentials: 'include', | |||
cache: 'no-store', | |||
body: JSON.stringify({ model, prompt: content }) | |||
}); | |||
const text = await r.text(); | |||
console.log('HTTP', r.status, r.statusText); | |||
console.log('RAW:', text); | |||
pending.remove?.(); | |||
if (!r.ok) { | |||
mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`); | |||
return; | |||
} | |||
let j; | |||
mpaiAppendMsg('error', ` | try { | ||
j = JSON.parse(text); | |||
if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} } | |||
console.log('PARSED:', j); | |||
} catch (e) { | |||
console.error('PARSE_ERR', e); | |||
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0, 1500)}`); | |||
return; | |||
} | |||
if (j && j.status === 'ok' && j.result) { | |||
mpaiState.history.push({ role: 'assistant', content: j.result }); | |||
mpaiAppendMsg('assistant', j.result); | |||
mpaiSaveLocal(); | |||
} else { | |||
mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0, 1500)}`); | |||
} | |||
} catch (e) { | } catch (e) { | ||
mpaiAppendMsg('error', ` | mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`); | ||
} | } | ||
}; | |||
/* ===================== SHOW UI + BIND UNA SOLA VOLTA ===================== */ | |||
window.showMpAI = function () { | |||
['api-settings', 'project-status', 'chatgpt-plus', 'test-tools', 'activity-log'] | |||
.forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; }); | |||
const ai = document.getElementById('mpAI'); | |||
if (ai) { ai.style.display = 'block'; ai.scrollIntoView({ behavior: 'smooth' }); } | |||
mpaiBindUI(); // assicura i listener una sola volta | |||
}; | |||
}; | |||
function mpaiBindUI() { | |||
if (window.__MP_AI_BOUND__) return; // evita doppi binding | |||
window.__MP_AI_BOUND__ = true; | |||
if (!$mp('#mpAI')) return; // UI non presente | |||
// Upload locale (solo elenco) | |||
const dz = $mp('#mpai-dropzone'); | |||
const fi = $mp('#mpai-file-input'); | |||
dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; }); | |||
dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; }); | |||
dz && dz.addEventListener('drop', e => { | |||
e.preventDefault(); dz.style.opacity = 1; | |||
Array.from(e.dataTransfer.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name })); | |||
mpaiRenderFiles(); | |||
}); | |||
fi && fi.addEventListener('change', () => { | |||
Array.from(fi.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name })); | |||
fi.value = ''; mpaiRenderFiles(); | |||
}); | |||
// Bottoni | |||
$mp('#mpai-send')?.addEventListener('click', window.sendPrompt); | |||
$mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject); | |||
$mp('#mpai-new-chat')?.addEventListener('click', () => { | |||
mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles(); | |||
const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; } | |||
if (mpaiState.currentProject) { mpaiAppendMsg('assistant', 'Nuova chat per "' + mpaiState.currentProject + '".'); mpaiSaveLocal(); } | |||
else { mpaiState.sessionId = null; mpaiState.sessionMeta = null; mpaiEnsureSession(); mpaiAppendMsg('assistant', 'Nuova conversazione.'); mpaiSaveSession(); } | |||
}); | |||
$mp('#mpai-clear')?.addEventListener('click', () => { | |||
if (!mpaiState.currentProject) { alert('Crea o seleziona un progetto'); return; } | |||
if (!confirm('Svuotare la conversazione locale?')) return; | |||
mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles(); | |||
const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; } | |||
mpaiAppendMsg('assistant', 'Conversazione pulita.'); mpaiSaveLocal(); | |||
}); | |||
// Invio con Cmd/Ctrl+Enter nella textarea | |||
document.addEventListener('keydown', (e) => { | |||
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { | |||
const ta = $mp('#mpai-input'); | |||
if (ta && ta === document.activeElement) { e.preventDefault(); window.sendPrompt(); } | |||
} | |||
}); | |||
// Prima boot | |||
mpaiLoadProjects(); | |||
console.log('🔗 MPAI listeners collegati.'); | |||
} | } | ||
// Bind on ready (se la UI è già presente) | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', () => { if ($mp('#mpAI')) mpaiBindUI(); }); | |||
} else { | |||
if ($mp('#mpAI')) mpaiBindUI(); | |||
} | |||
console.log('✅ MPAI: pronta.'); | console.log('✅ MPAI: pronta.'); | ||
})(); | })(); | ||
Versione delle 08:04, 30 set 2025
/* ===================== MPAI – CHAT APP (no inline script) ===================== */
/** Guardia anti-doppio-caricamento (strumentata) */
if (window.__MP_DASHBOARD_LOADED__) {
const src = (document.currentScript && document.currentScript.src) || '(inline)';
try {
const u = new URL(src, location.origin);
console.warn('MP Dashboard già caricata – stop. Source:', src, 'from=', u.searchParams.get('from') || '(n/a)');
} catch {
console.warn('MP Dashboard già caricata – stop. Source:', src);
}
console.trace('Stack doppio load');
throw new Error('MP Dashboard già caricata');
}
window.__MP_DASHBOARD_LOADED__ = true;
/** Se qualche script ha rotto $, lo ri-aggancio a jQuery */
if (!(window.$ && window.$.fn && typeof window.$.Deferred === 'function')) {
window.$ = window.jQuery;
}
/** Shortcut locale */
window.$mp = window.$mp || (s => document.querySelector(s));
(function () {
console.log('🧭 MPAI: init…');
/* ===================== STATO ===================== */
const mpaiState = {
currentProject: null, // nome progetto (se selezionato)
sessionId: null, // id bozza locale
sessionMeta: null, // {title, updated}
history: [],
attachments: []
};
/* ===================== UI HELPERS ===================== */
function mpaiAppendMsg(role, content) {
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
const div = document.createElement('div');
div.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error'));
div.innerHTML = `
<div class="role">${role==='user'?'Tu':role==='assistant'?'GPT':'Errore'}</div>
<div class="content" style="white-space:pre-wrap">${content}</div>`;
chatEl.appendChild(div);
chatEl.scrollTop = chatEl.scrollHeight;
}
function mpaiRenderFiles() {
const fileListEl = $mp('#mpai-file-list');
if (!fileListEl) return;
fileListEl.innerHTML = '';
mpaiState.attachments.forEach((a, i) => {
const pill = document.createElement('span');
pill.className = 'mpai-filepill';
pill.innerHTML = `<span title="Allegato">${a.name}</span><button title="Rimuovi">✕</button>`;
pill.querySelector('button').onclick = () => { mpaiState.attachments.splice(i, 1); mpaiRenderFiles(); };
fileListEl.appendChild(pill);
});
}
/* ===================== PERSISTENZA ===================== */
function mpaiSaveLocal() {
if (!mpaiState.currentProject) return;
localStorage.setItem('mpai.hist.' + mpaiState.currentProject, JSON.stringify(mpaiState.history.slice(-200)));
}
function mpaiLoadLocal(project) {
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
const raw = localStorage.getItem('mpai.hist.' + project);
mpaiState.history = raw ? JSON.parse(raw) : [];
chatEl.innerHTML = '';
if (!mpaiState.history.length) {
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "' + project + '". Allegami anche file sanificati se servono.');
} else {
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
}
}
function mpaiEnsureSession() {
if (!mpaiState.sessionId) {
mpaiState.sessionId = 'sess-' + Date.now();
mpaiState.sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
}
}
function mpaiSaveSession() {
if (!mpaiState.sessionId) return;
const key = 'mpai.session.hist.' + mpaiState.sessionId;
localStorage.setItem(key, JSON.stringify(mpaiState.history.slice(-200)));
mpaiState.sessionMeta.updated = Date.now();
localStorage.setItem('mpai.session.meta.' + mpaiState.sessionId, JSON.stringify(mpaiState.sessionMeta));
}
function mpaiLoadSession(id) {
const chatEl = $mp('#mpai-chat');
if (!chatEl) return;
mpaiState.sessionId = id;
const key = 'mpai.session.hist.' + id;
const raw = localStorage.getItem(key);
mpaiState.history = raw ? JSON.parse(raw) : [];
const metaRaw = localStorage.getItem('mpai.session.meta.' + id);
mpaiState.sessionMeta = metaRaw ? JSON.parse(metaRaw) : { title: 'Nuova conversazione', updated: Date.now() };
chatEl.innerHTML = '';
if (!mpaiState.history.length) {
mpaiAppendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
} else {
mpaiState.history.forEach(m => mpaiAppendMsg(m.role, m.content));
}
}
/* ===================== PROGETTI (sidebar) ===================== */
async function mpaiLoadProjects() {
const box = $mp('#mpai-project-list');
if (!box) return;
box.innerHTML = '<em class="muted">Carico progetti…</em>';
try {
const r = await fetch('/dashboard/api/project_list.php', { cache: 'no-store', credentials: 'include' });
const j = await r.json();
const arr = Array.isArray(j) ? j : (Array.isArray(j.projects) ? j.projects : []);
if (!arr.length) { box.innerHTML = '<em class="muted">Nessun progetto</em>'; return; }
box.innerHTML = '';
arr.forEach(item => {
const name = (typeof item === 'string') ? item : (item.name || item);
const row = document.createElement('div');
row.className = 'row';
row.innerHTML = `<span class="title">${name}</span>
<span><button class="mpai-btn" data-open="${name}">Apri</button></span>`;
row.querySelector('[data-open]').onclick = () => {
mpaiState.currentProject = name;
mpaiLoadLocal(name);
};
box.appendChild(row);
});
if (!mpaiState.currentProject) {
const first = (typeof arr[0] === 'string') ? arr[0] : (arr[0].name || arr[0]);
mpaiState.currentProject = first;
mpaiLoadLocal(first);
}
} catch (e) {
box.innerHTML = '<span class="muted">Errore caricamento: ' + e.message + '</span>';
}
}
async function mpaiCreateProject() {
const inp = $mp('#mpai-project-name');
const name = (inp?.value || '').trim();
if (!name) { alert('Inserisci un nome progetto'); return; }
try {
const r = await fetch('/dashboard/api/project_create.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ name })
});
const txt = await r.text(); let j; try { j = JSON.parse(txt) } catch { j = { ok: false, raw: txt }; }
if (j.ok !== false) { await mpaiLoadProjects(); mpaiState.currentProject = name; mpaiState.history = []; mpaiLoadLocal(name); inp.value = ''; }
else { alert('Errore creazione: ' + (j.error || '')); }
} catch (e) { alert('Errore rete: ' + e.message); }
}
/* ===================== INVIO PROMPT → PROXY ===================== */
window.sendPrompt = async function () {
try {
const ta = document.querySelector('#mpai-input');
if (!ta) return;
const content = (ta.value || '').trim();
if (!content) { ta.focus(); return; }
// mostra subito il messaggio utente
mpaiState.history.push({ role: 'user', content });
mpaiAppendMsg('user', content);
ta.value = '';
const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
// placeholder "Elaboro…"
const pending = document.createElement('div');
pending.className = 'mpai-msg';
pending.innerHTML = '<em class="muted">Elaboro…</em>';
document.querySelector('#mpai-chat')?.appendChild(pending);
const r = await fetch('/dashboard/api/openai_project_gpt.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
cache: 'no-store',
body: JSON.stringify({ model, prompt: content })
});
const text = await r.text();
console.log('HTTP', r.status, r.statusText);
console.log('RAW:', text);
pending.remove?.();
if (!r.ok) {
mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`);
return;
}
let j;
try {
j = JSON.parse(text);
if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} }
console.log('PARSED:', j);
} catch (e) {
console.error('PARSE_ERR', e);
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0, 1500)}`);
return;
}
if (j && j.status === 'ok' && j.result) {
mpaiState.history.push({ role: 'assistant', content: j.result });
mpaiAppendMsg('assistant', j.result);
mpaiSaveLocal();
} else {
mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0, 1500)}`);
}
} catch (e) {
mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`);
}
};
/* ===================== SHOW UI + BIND UNA SOLA VOLTA ===================== */
window.showMpAI = function () {
['api-settings', 'project-status', 'chatgpt-plus', 'test-tools', 'activity-log']
.forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; });
const ai = document.getElementById('mpAI');
if (ai) { ai.style.display = 'block'; ai.scrollIntoView({ behavior: 'smooth' }); }
mpaiBindUI(); // assicura i listener una sola volta
};
function mpaiBindUI() {
if (window.__MP_AI_BOUND__) return; // evita doppi binding
window.__MP_AI_BOUND__ = true;
if (!$mp('#mpAI')) return; // UI non presente
// Upload locale (solo elenco)
const dz = $mp('#mpai-dropzone');
const fi = $mp('#mpai-file-input');
dz && dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
dz && dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
dz && dz.addEventListener('drop', e => {
e.preventDefault(); dz.style.opacity = 1;
Array.from(e.dataTransfer.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
mpaiRenderFiles();
});
fi && fi.addEventListener('change', () => {
Array.from(fi.files || []).forEach(f => mpaiState.attachments.push({ file: f, name: f.name }));
fi.value = ''; mpaiRenderFiles();
});
// Bottoni
$mp('#mpai-send')?.addEventListener('click', window.sendPrompt);
$mp('#mpai-project-create')?.addEventListener('click', mpaiCreateProject);
$mp('#mpai-new-chat')?.addEventListener('click', () => {
mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
if (mpaiState.currentProject) { mpaiAppendMsg('assistant', 'Nuova chat per "' + mpaiState.currentProject + '".'); mpaiSaveLocal(); }
else { mpaiState.sessionId = null; mpaiState.sessionMeta = null; mpaiEnsureSession(); mpaiAppendMsg('assistant', 'Nuova conversazione.'); mpaiSaveSession(); }
});
$mp('#mpai-clear')?.addEventListener('click', () => {
if (!mpaiState.currentProject) { alert('Crea o seleziona un progetto'); return; }
if (!confirm('Svuotare la conversazione locale?')) return;
mpaiState.history = []; mpaiState.attachments = []; mpaiRenderFiles();
const chatEl = $mp('#mpai-chat'); if (chatEl) { chatEl.innerHTML = ''; }
mpaiAppendMsg('assistant', 'Conversazione pulita.'); mpaiSaveLocal();
});
// Invio con Cmd/Ctrl+Enter nella textarea
document.addEventListener('keydown', (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
const ta = $mp('#mpai-input');
if (ta && ta === document.activeElement) { e.preventDefault(); window.sendPrompt(); }
}
});
// Prima boot
mpaiLoadProjects();
console.log('🔗 MPAI listeners collegati.');
}
// Bind on ready (se la UI è già presente)
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => { if ($mp('#mpAI')) mpaiBindUI(); });
} else {
if ($mp('#mpAI')) mpaiBindUI();
}
console.log('✅ MPAI: pronta.');
})();