MediaWiki:CommonDashboard.js: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 195: | Riga 195: | ||
} | } | ||
/** INVIO PROMPT → OpenAI proxy (versione robusta) */ | /** INVIO PROMPT → OpenAI proxy (versione robusta) */ | ||
window.sendPrompt = async function(){ | window.sendPrompt = async function () { | ||
try{ | try { | ||
const ta = document.querySelector('#mpai-input'); | const ta = document.querySelector('#mpai-input'); | ||
if (!ta) return; | if (!ta) return; | ||
const content = (ta.value || '').trim(); | const content = (ta.value || '').trim(); | ||
if (!content){ ta.focus(); return; | if (!content) { ta.focus(); return; } | ||
// | // mostra subito il messaggio utente | ||
mpaiAppendMsg('user', content); | mpaiAppendMsg('user', content); | ||
ta.value = ''; | ta.value = ''; | ||
const model = (document.querySelector('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim(); | |||
const | |||
// | // placeholder "Elaboro…" | ||
const pending = document.createElement('div'); | const pending = document.createElement('div'); | ||
pending.className = 'mpai-msg'; | pending.className = 'mpai-msg'; | ||
pending.innerHTML = '<em class="muted">Elaboro…</em>'; | pending.innerHTML = '<em class="muted">Elaboro…</em>'; | ||
document.querySelector('#mpai-chat')?.appendChild(pending); | |||
const r = await fetch('/dashboard/api/openai_project_gpt.php', { | const r = await fetch('/dashboard/api/openai_project_gpt.php', { | ||
method: 'POST', | method: 'POST', | ||
headers: { 'Content-Type': 'application/json' }, | headers: {'Content-Type': 'application/json'}, | ||
credentials: 'include', | credentials: 'include', | ||
cache: 'no-store', | cache: 'no-store', | ||
body: JSON.stringify({ model, prompt: | body: JSON.stringify({ model, prompt: content }) | ||
}); | }); | ||
const | const text = await r.text(); | ||
pending.remove?.(); | pending.remove?.(); | ||
if (!r.ok){ | if (!r.ok) { | ||
mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${text || '(nessun body)'}`); | |||
mpaiAppendMsg('error', `HTTP ${r.status} ${r.statusText}\n\n${ | |||
return; | return; | ||
} | } | ||
let j; | let j; | ||
try { | try { | ||
j = JSON.parse( | j = JSON.parse(text); | ||
if (typeof j === 'string') { | if (typeof j === 'string') { try { j = JSON.parse(j); } catch {} } // tollera doppia codifica | ||
} catch (e) { | } catch (e) { | ||
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0,1500)}`); | |||
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${ | |||
return; | return; | ||
} | } | ||
if (j && j.status === 'ok' && j.result){ | if (j && j.status === 'ok' && j.result) { | ||
mpaiAppendMsg('assistant', j.result); | mpaiAppendMsg('assistant', j.result); | ||
} else { | } else { | ||
mpaiAppendMsg('error', `API error: ${(j && (j.error || j.message)) || 'sconosciuto'}\n\nRAW:\n${text.slice(0,1500)}`); | |||
} | } | ||
} catch(e){ | } catch (e) { | ||
mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`); | mpaiAppendMsg('error', `Errore di rete/JS: ${e.message}`); | ||
} | } | ||
}; | }; | ||
Versione delle 07:47, 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;
}
// Evita duplicato $mp: esponilo solo se non esiste
window.$mp = window.$mp || (s => document.querySelector(s));
// 4) WRAPPER: tutto il codice MPAI sta DENTRO questa IIFE.
// Qui dentro puoi dichiarare tranquillamente qs/qsa: sono LOCALI (non globali).
(function () {
console.log('🧭 MPAI: init…');
// Helper VANILLA **LOCALI** (non globali, niente conflitti)
const qs = (sel, root=document) => root.querySelector(sel);
const qsa = (sel, root=document) => Array.from(root.querySelectorAll(sel));
/* ============================================================
MPAI – CHAT APP (no inline script)
=============================================================== */
/** Mostra la UI AI e nasconde i box legacy */
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'}); }
};
/** Stato locale della chat */
const mpaiState = {
currentProject: null, // “progetto reale” (opzionale)
sessionId: null, // id bozza locale
sessionMeta: null, // {title, updated}
history: [],
attachments: []
};
/** Shortcuts */
const $mp = (s)=>document.querySelector(s);
/** 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 “per progetto” (cronologia locale) */
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));
}
}
/** Bozze locali (se non c’è progetto) */
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));
}
}
/** Lista progetti (sidebar AI) – opzionale ma comoda */
async function mpaiLoadProjects(){
const box = $mp('#mpai-project-list');
if (!box) return; // se non c’è la UI AI, non fare nulla
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 → OpenAI proxy (versione robusta) */
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
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();
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 {} } // tollera doppia codifica
} catch (e) {
mpaiAppendMsg('error', `Risposta non in JSON:\n\n${text.slice(0,1500)}`);
return;
}
if (j && j.status === 'ok' && j.result) {
mpaiAppendMsg('assistant', j.result);
} 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}`);
}
};
/** Bind UI quando la pagina è pronta (solo se la UI AI esiste) */
document.addEventListener('DOMContentLoaded', ()=>{
if (!$mp('#mpAI')) return; // non siamo nella dashboard AI
// 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(); } }
});
// Boot
mpaiLoadProjects();
});
console.log('✅ MPAI: pronta.');
})();