Nessun oggetto della modifica
Nessun oggetto della modifica
Riga 13: Riga 13:
   <button class="dashboard-toggle" onclick="toggleDashboardBox('test-tools')">🧪 Strumenti di Test</button>
   <button class="dashboard-toggle" onclick="toggleDashboardBox('test-tools')">🧪 Strumenti di Test</button>
   <button class="dashboard-toggle" onclick="toggleDashboardBox('activity-log')">📘 Registro Attività</button>
   <button class="dashboard-toggle" onclick="toggleDashboardBox('activity-log')">📘 Registro Attività</button>
 
 
   <button class="dashboard-toggle"
   <button class="dashboard-toggle"
  onclick="(window.showMpAI ? showMpAI() : (function(){var ai=document.getElementById('mpAI'); if(ai) ai.style.display='block';})())">
          onclick="(window.showMpAI ? showMpAI() : (function(){var ai=document.getElementById('mpAI'); if(ai) ai.style.display='block';})())">
  🤖 Masticationpedia AI
     🤖 Masticationpedia AI
</button>
   </button>
 
</div>
 
<script>
/* fallback: sempre disponibile e definita presto */
window.showMpAI = function(){
  // nascondi eventuali box legacy
  ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
     .forEach(function(id){ var el=document.getElementById(id); if(el) el.style.display='none'; });
  // mostra l'AI
  var ai = document.getElementById('mpAI');
  if (ai) { ai.style.display='block'; }
  else { alert('Elemento #mpAI non trovato nella pagina.'); }
};
</script>
 
 
 
 
<!-- ============== MASTICATIONPEDIA AI (schermata unica) ============== -->
<div id="mpAI" class="mpai-root" style="display:none">
  <!-- Header -->
  <div class="mpai-header">
    <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div>
    <div class="mpai-actions">
      <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button>
      <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button>
      <button id="mpai-save-as-project" class="mpai-btn">💾 Salva come progetto</button>
 
    </div>
  </div>
 
   <div class="mpai-grid">
    <!-- Sidebar sinistra: Progetti -->
    <aside class="mpai-col mpai-left">
      <h4>📂 Progetti</h4>
      <div id="mpai-project-list" class="mpai-projects">
        <em class="muted">Carico progetti…</em>
      </div>
      <div class="mpai-new-project">
        <input id="mpai-project-name" type="text" placeholder="Nuovo progetto (A-Z 0-9 _ -)" />
        <button id="mpai-project-create" class="mpai-btn">Crea</button>
      </div>
      <div class="mpai-help muted">
        Ogni progetto mantiene la sua cronologia locale (browser).
      </div>
    </aside>
 
    <!-- Chat centrale -->
    <main class="mpai-col mpai-center">
      <!-- Allegati (solo elenco locale; invio server lo aggiungiamo in step 2) -->
      <div id="mpai-uploads" class="mpai-uploads" data-state="idle">
        <div class="mpai-dropzone" id="mpai-dropzone">
          <div>
            <div class="muted" style="margin-bottom:6px;">Trascina qui file <b>sanificati</b> oppure</div>
            <label class="mpai-file-label">
              <input id="mpai-file-input" type="file" multiple style="display:none;">
              <span class="mpai-btn">📂 Aggiungi file</span>
            </label>
          </div>
        </div>
        <div id="mpai-file-list" class="mpai-filelist"></div>
      </div>
 
      <!-- Messaggi -->
      <div id="mpai-chat" class="mpai-chat">
        <div class="mpai-msg mpai-hint">
          Benvenuto. Seleziona un progetto a sinistra, allega eventuali file <b>sanificati</b> e fai la tua domanda.
          Le risposte appariranno qui sotto in ordine, come in ChatGPT.
        </div>
      </div>
 
      <!-- Prompt -->
      <div class="mpai-composer">
        <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea>
        <button id="mpai-send" class="mpai-btn primary">Invia</button>
      </div>
    </main>
 
    <!-- Sidebar destra: Impostazioni -->
    <aside class="mpai-col mpai-right">
      <h4>⚙️ Impostazioni</h4>
      <label class="mpai-field">Modello
        <select id="mpai-model">
          <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
          <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
        </select>
      </label>
      <label class="mpai-field">Temperatura
        <input id="mpai-temp" type="number" min="0" max="1" step="0.1" value="0.7">
      </label>
      <label class="mpai-check">
        <input id="mpai-sanitized-only" type="checkbox" checked>
        Usa solo file <b>sanificati</b> come contesto
      </label>
      <div class="mpai-help muted">
        Le API key restano lato server (file sicuro). Il browser non vede mai le chiavi.
      </div>
    </aside>
  </div>
</div>
</div>
<!-- ============ STILI (CSS) ============ -->
<style>
  .mpai-root{--bg:#f7f8fa;--card:#fff;--muted:#6b7280;--line:#e5e7eb;--text:#111827;
    --primary:#0a7cff;--danger:#e74c3c;--accent:#eef2ff;
    font-family: system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif; color:var(--text);}
  .mpai-header{display:flex;align-items:center;justify-content:space-between;margin:8px 0 12px}
  .mpai-title{font-size:20px;font-weight:700}
  .mpai-actions{display:flex;gap:8px}
  .mpai-btn{border:1px solid var(--line);background:var(--card);padding:.45rem .8rem;border-radius:8px;cursor:pointer}
  .mpai-btn:hover{background:#f3f4f6}
  .mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff}
  .mpai-btn.danger{background:var(--danger);border-color:var(--danger);color:#fff}
  .mpai-btn.ghost{background:transparent}
  .muted{color:var(--muted)}
  .mpai-grid{display:grid;grid-template-columns:260px minmax(0,1fr) 260px;gap:12px}
  .mpai-col{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:12px}
  .mpai-left,.mpai-right{max-height:75vh;overflow:auto}
  .mpai-projects{display:flex;flex-direction:column;gap:6px;margin:8px 0}
  .mpai-projects .row{display:flex;align-items:center;gap:6px;justify-content:space-between;border:1px solid var(--line);border-radius:8px;padding:6px 8px}
  .mpai-projects .row .title{font-family:monospace;font-size:12px}
  .mpai-new-project{display:flex;gap:6px;margin-top:8px}
  .mpai-new-project input{flex:1 1 auto;padding:.45rem .6rem;border:1px solid var(--line);border-radius:8px}
  .mpai-field{display:flex;flex-direction:column;gap:4px;margin:8px 0}
  .mpai-field input,.mpai-field select{padding:.45rem .6rem;border:1px solid var(--line);border-radius:8px}
  .mpai-check{display:flex;align-items:center;gap:8px;margin:8px 0}
  .mpai-center{display:flex;flex-direction:column;gap:10px}
  .mpai-uploads{border:1px dashed var(--line);border-radius:12px;padding:10px;background:#fafafa}
  .mpai-dropzone{display:flex;align-items:center;justify-content:center;border-radius:10px;padding:12px;background:var(--accent);text-align:center}
  .mpai-filelist{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}
  .mpai-filepill{display:flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:999px;padding:4px 8px;background:#fff;font-size:12px}
  .mpai-filepill button{border:none;background:transparent;cursor:pointer;color:#c00}
  .mpai-chat{display:flex;flex-direction:column;gap:10px;max-height:50vh;overflow:auto;border:1px solid var(--line);border-radius:12px;padding:10px;background:#fff}
  .mpai-msg{border:1px solid var(--line);border-radius:12px;padding:10px;background:#fff}
  .mpai-msg.user{border-color:#bee3f8;background:#eff6ff}
  .mpai-msg.assistant{border-color:#d1fae5;background:#f0fdf4}
  .mpai-msg.error{border-color:#fecaca;background:#fff1f2}
  .mpai-msg .role{font-weight:600;margin-bottom:4px}
  .mpai-hint{border-style:dashed;color:var(--muted)}
  .mpai-composer{display:flex;gap:8px;align-items:flex-end}
  .mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px}
  @media (max-width: 1100px){
    .mpai-grid{grid-template-columns:1fr}
    .mpai-left,.mpai-right{max-height:none}
  }
</style>
<!-- ============ JS (tutto qui) ============ -->


<script>
<script>
/* -------- Fallback utili (non rompersi se mancano) -------- */
/* ===== Fallback/utility sempre caricati ===== */
function toggleDashboardBox(id){
function toggleDashboardBox(id){
   var el = document.getElementById(id);
   var el = document.getElementById(id);
   if (!el) return;
   if (!el) return;
  // nascondo AI se apro altro box
   var ai = document.getElementById('mpAI'); if (ai) ai.style.display='none';
   var ai = document.getElementById('mpAI'); if (ai) ai.style.display='none';
   el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none';
   el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none';
Riga 185: Riga 36:
}
}
function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); }
function uploadToOpenAI(){ alert('Prossimo step: upload verso OpenAI.'); }
 
window.showMpAI = function(){
/* --- mostra la nuova UI AI e nasconde gli altri box --- */
function showMpAI(){
   ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
   ['api-settings','project-status','chatgpt-plus','test-tools','activity-log']
     .forEach(id => { var el = document.getElementById(id); if (el) el.style.display='none'; });
     .forEach(function(id){ var el=document.getElementById(id); if(el) el.style.display='none'; });
   var ai = document.getElementById('mpAI');
   var ai = document.getElementById('mpAI');
   if (ai) ai.style.display = 'block';
   if (ai) ai.style.display='block';
}
};


/* ========= MPAI APP ========= */
/* ========= MPAI APP ========= */
Riga 202: Riga 51:
   let history = [];
   let history = [];
   let attachments = [];
   let attachments = [];


   // Shortcuts
   // Shortcuts
Riga 227: Riga 75:
     chatEl.innerHTML = '';
     chatEl.innerHTML = '';
     if (!history.length){
     if (!history.length){
       appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Allegami anche file sanificati se servono.');
       appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare su "'+project+'". Puoi allegare file sanificati.');
     } else {
     } else {
       history.forEach(m => appendMsg(m.role, m.content));
       history.forEach(m => appendMsg(m.role, m.content));
Riga 243: Riga 91:
   }
   }


// ======== Sessioni locali (bozze senza progetto) ========
  /* ===== Sessioni locali (bozze) ===== */
 
  function ensureSession(){
function makeAutoTitle(s){
    if (!sessionId){
  if (!s) return 'Nuova conversazione';
      sessionId = 'sess-' + Date.now();
  s = (s||'').replace(/\s+/g,' ').trim();
      sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
  const firstSentence = (s.split(/[.?!]/)[0] || s).trim();
      localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta));
  const short = firstSentence.split(' ').slice(0,8).join(' ');
    }
  return (short.length > 60 ? short.slice(0,57)+'…' : short) || 'Nuova conversazione';
  }
}
  function saveSession(){
 
    if (!sessionId) return;
function ensureSession(){
     const key = 'mpai.session.hist.' + sessionId;
  if (!sessionId){
    localStorage.setItem(key, JSON.stringify(history.slice(-200)));
     sessionId = 'sess-' + Date.now();
     sessionMeta.updated = Date.now();
     sessionMeta = { title: 'Nuova conversazione', updated: Date.now() };
     localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta));
     localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta));
   }
   }
}
   function loadSession(id){
 
    sessionId = id;
function saveSession(){
    const key = 'mpai.session.hist.' + id;
  if (!sessionId) return;
    const raw = localStorage.getItem(key);
   const key = 'mpai.session.hist.' + sessionId;
    history = raw ? JSON.parse(raw) : [];
  localStorage.setItem(key, JSON.stringify(history.slice(-200)));
    const metaRaw = localStorage.getItem('mpai.session.meta.'+id);
  sessionMeta.updated = Date.now();
    sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()};
  localStorage.setItem('mpai.session.meta.'+sessionId, JSON.stringify(sessionMeta));
    chatEl.innerHTML = '';
}
    if (!history.length){
 
      appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
function loadSession(id){
    } else {
  sessionId = id;
      history.forEach(m => appendMsg(m.role, m.content));
  const key = 'mpai.session.hist.' + id;
    }
  const raw = localStorage.getItem(key);
  history = raw ? JSON.parse(raw) : [];
  const metaRaw = localStorage.getItem('mpai.session.meta.'+id);
  sessionMeta = metaRaw ? JSON.parse(metaRaw) : {title:'Nuova conversazione', updated:Date.now()};
  chatEl.innerHTML = '';
  if (!history.length){
    appendMsg('assistant', 'Pronto! Dimmi cosa vuoi fare. Puoi allegare file sanificati.');
  } else {
    history.forEach(m => appendMsg(m.role, m.content));
   }
   }
}
  function resetToNewChat(){
 
    history = [];
function resetToNewChat(){
    attachments = [];
  history = [];
    renderFiles();
  attachments = [];
    chatEl.innerHTML = '';
  renderFiles();
    if (currentProject){
  chatEl.innerHTML = '';
      appendMsg('assistant','Nuova chat per "'+currentProject+'".');
  if (currentProject){
      saveLocal();
    appendMsg('assistant','Nuova chat per "'+currentProject+'".');
    } else {
    saveLocal();
      sessionId = null; sessionMeta = null;
  } else {
      ensureSession();
    sessionId = null; sessionMeta = null;
      appendMsg('assistant','Nuova conversazione.');
    ensureSession();
      saveSession();
    appendMsg('assistant','Nuova conversazione.');
    }
    saveSession();
   }
   }
}


 
   /* ===== Progetti (lista/crea) ===== */
 
 
 
 
 
   // Progetti
   async function loadProjects(){
   async function loadProjects(){
     const box = $('#mpai-project-list');
     const box = $('#mpai-project-list');
    if (!box) return;
     box.innerHTML = '<em class="muted">Carico progetti…</em>';
     box.innerHTML = '<em class="muted">Carico progetti…</em>';
     try{
     try{
Riga 325: Riga 156:
         box.appendChild(row);
         box.appendChild(row);
       });
       });
       if (!currentProject){ currentProject = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]); loadLocal(currentProject); }
       if (!currentProject){
        const first = (typeof arr[0]==='string')?arr[0]:(arr[0].name||arr[0]);
        currentProject = first;
        loadLocal(first);
      }
     }catch(e){
     }catch(e){
       box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>';
       box.innerHTML = '<span class="muted">Errore caricamento: '+e.message+'</span>';
Riga 347: Riga 182:
   }
   }


   // Upload locale
   /* ===== Upload locale (visuale) ===== */
   const dz = $('#mpai-dropzone');
   const dz = $('#mpai-dropzone');
   const fi = $('#mpai-file-input');
   const fi = $('#mpai-file-input');
   dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
   if (dz){
  dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
    dz.addEventListener('dragover', e => { e.preventDefault(); dz.style.opacity = .85; });
  dz.addEventListener('drop', e => {
    dz.addEventListener('dragleave', () => { dz.style.opacity = 1; });
    e.preventDefault(); dz.style.opacity = 1;
    dz.addEventListener('drop', e => {
    Array.from(e.dataTransfer.files||[]).forEach(f=> attachments.push({file:f,name:f.name}));
      e.preventDefault(); dz.style.opacity = 1;
    renderFiles();
      Array.from(e.dataTransfer.files||[]).forEach(f=> attachments.push({file:f,name:f.name}));
  });
      renderFiles();
   fi.addEventListener('change', () => {
    });
    Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name}));
   }
    fi.value=''; renderFiles();
  if (fi){
  });
    fi.addEventListener('change', () => {
      Array.from(fi.files||[]).forEach(f=> attachments.push({file:f,name:f.name}));
      fi.value=''; renderFiles();
    });
  }


   // Invio prompt
   /* ===== Invio prompt (via proxy sicuro) ===== */
   async function sendPrompt(){
   async function sendPrompt(){
     const ta = $('#mpai-input');
     const ta = $('#mpai-input');
     const model = ($('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
     const model = ($('#mpai-model')?.value || 'gpt-4o-2024-05-13').trim();
     const temp  = parseFloat($('#mpai-temp')?.value || '0.7') || 0.7;
     const temp  = parseFloat($('#mpai-temp')?.value || '0.7') || 0.7;
     const sanitizedOnly = $('#mpai-sanitized-only')?.checked;
     const sanitizedOnly = !!($('#mpai-sanitized-only')?.checked);
     const content = (ta.value||'').trim();
 
     const content = (ta.value || '').trim();
     if (!content){ ta.focus(); return; }
     if (!content){ ta.focus(); return; }
    if (!currentProject){ alert('Seleziona o crea un progetto (colonna sinistra).'); return; }


    // Se non c'è un progetto, abilita la bozza locale
    if (!currentProject && !sessionId){ ensureSession(); loadSession(sessionId); }
    // Mostra subito il messaggio dell'utente
     history.push({role:'user', content});
     history.push({role:'user', content});
     appendMsg('user', content);
     appendMsg('user', content);
     ta.value='';
     ta.value = '';
 
    // Contesto: progetto o sessione locale
    const contextName = currentProject
      ? `Progetto: ${currentProject}`
      : `Sessione: ${sessionMeta?.title || 'Nuova conversazione'}`;


     const fileNames = attachments.map(a=>a.name).join(', ');
     const fileNames = attachments.map(a => a.name).join(', ');
     const preface = `Contesto progetto: ${currentProject}
     const preface = `${contextName}
File allegati${sanitizedOnly?' (sanificati)':''}: ${fileNames || 'nessuno'}
File allegati${sanitizedOnly ? ' (sanificati)' : ''}: ${fileNames || 'nessuno'}


Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}.
Istruzioni: rispondi in ITALIANO, struttura chiara (titoli, punti elenco), sii operativo. Temperatura: ${temp}.
Riga 385: Riga 233:
${content}`;
${content}`;


    // Placeholder “Elaboro…”
     const pending = document.createElement('div');
     const pending = document.createElement('div');
     pending.className='mpai-msg'; pending.innerHTML='<em class="muted">Elaboro…</em>';
     pending.className = 'mpai-msg';
     chatEl.appendChild(pending); chatEl.scrollTop=chatEl.scrollHeight;
    pending.innerHTML = '<em class="muted">Elaboro…</em>';
     chatEl.appendChild(pending);
    chatEl.scrollTop = chatEl.scrollHeight;


     try{
     try{
       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', // per Basic Auth
         body: JSON.stringify({ model, prompt: preface })
         body: JSON.stringify({ model, prompt: preface })
       });
       });
       const txt = await r.text(); let j; try{ j=JSON.parse(txt) }catch{ j={status:'error',raw:txt}; }
 
       const txt = await r.text();
      let j; try{ j = JSON.parse(txt); } catch { j = { status:'error', raw: txt }; }
 
       pending.remove();
       pending.remove();
       if (j.status==='ok' && j.result){
 
         history.push({role:'assistant', content:j.result});
       if (j.status === 'ok' && j.result){
         appendMsg('assistant', j.result); saveLocal();
        if (!currentProject && sessionMeta && sessionMeta.title === 'Nuova conversazione'){
       }else{
          sessionMeta.title = (content.slice(0, 48) || 'Nuova conversazione');
          saveSession();
        }
         history.push({role:'assistant', content: j.result});
         appendMsg('assistant', j.result);
        saveLocal();
       } else {
         const err = j.error || 'Errore sconosciuto';
         const err = j.error || 'Errore sconosciuto';
         history.push({role:'error', content:err});
         history.push({role:'error', content: err});
         appendMsg('error', err + (j.raw?'\n\nRAW:\n'+j.raw:''));
         appendMsg('error', err + (j.raw ? '\n\nRAW:\n' + j.raw : ''));
         saveLocal();
         saveLocal();
       }
       }
     }catch(e){
     } catch(e){
       pending.remove(); history.push({role:'error', content:e.message}); appendMsg('error', e.message); saveLocal();
       pending.remove();
      history.push({role:'error', content: e.message});
      appendMsg('error', e.message);
      saveLocal();
     }
     }
   }
   }


   // Bind
   /* ===== Bind UI ===== */
   document.addEventListener('click', (e)=>{
   document.addEventListener('click', (e)=>{
     if (e.target && e.target.id==='mpai-send') sendPrompt();
     if (e.target && e.target.id === 'mpai-send') sendPrompt();
     if (e.target && e.target.id==='mpai-project-create') createProject();
     if (e.target && e.target.id === 'mpai-project-create') createProject();
     if (e.target && e.target.id==='mpai-new-chat'){
     if (e.target && e.target.id === 'mpai-new-chat') resetToNewChat();
      if (!currentProject){ alert('Crea o seleziona un progetto'); return; }
     if (e.target && e.target.id === 'mpai-clear') resetToNewChat();
      history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Nuova chat per "'+currentProject+'".'); saveLocal();
    // (in futuro: mpai-save-as-project)
    }
     if (e.target && e.target.id==='mpai-clear'){
      if (!currentProject){ alert('Crea o seleziona un progetto'); return; }
      if (!confirm('Svuotare la conversazione locale?')) return;
      history=[]; attachments=[]; renderFiles(); chatEl.innerHTML=''; appendMsg('assistant','Conversazione pulita.'); saveLocal();
    }
   });
   });
   document.addEventListener('keydown', (e)=>{
   document.addEventListener('keydown', (e)=>{
     if ((e.metaKey||e.ctrlKey) && e.key==='Enter'){ const ta=$('#mpai-input'); if (ta && ta===document.activeElement){ e.preventDefault(); sendPrompt(); } }
     if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
      const ta = $('#mpai-input');
      if (ta && ta === document.activeElement) { e.preventDefault(); sendPrompt(); }
    }
   });
   });


// Boot
  /* ===== Boot ===== */
loadProjects();
  loadProjects();
  if (!currentProject) { ensureSession(); loadSession(sessionId); }


// Se non c'è ancora un progetto attivo → crea una sessione locale "bozza"
})(); // chiude l’IIFE
if (!currentProject){
  ensureSession();
  loadSession(sessionId);
}
})();
</script>
</script>


<!-- ============== MASTICATIONPEDIA AI (schermata unica) ============== -->
<div id="mpAI" class="mpai-root" style="display:none">
  <!-- Header -->
  <div class="mpai-header">
    <div class="mpai-title">🤖 Masticationpedia <b>AI</b></div>
    <div class="mpai-actions">
      <button id="mpai-new-chat" class="mpai-btn ghost">Nuova chat</button>
      <button id="mpai-clear" class="mpai-btn danger">Pulisci conversazione</button>
      <button id="mpai-save-as-project" class="mpai-btn">💾 Salva come progetto</button>
    </div>
  </div>


<!-- ============== /MASTICATIONPEDIA AI ============== -->
  <div class="mpai-grid">
    <!-- Sidebar sinistra: Progetti -->
    <aside class="mpai-col mpai-left">
      <h4>📂 Progetti</h4>
      <div id="mpai-project-list" class="mpai-projects">
        <em class="muted">Carico progetti…</em>
      </div>
      <div class="mpai-new-project">
        <input id="mpai-project-name" type="text" placeholder="Nuovo progetto (A-Z 0-9 _ -)" />
        <button id="mpai-project-create" class="mpai-btn">Crea</button>
      </div>
      <div class="mpai-help muted">
        Ogni progetto mantiene la sua cronologia locale (browser).
      </div>
    </aside>
 
    <!-- Chat centrale -->
    <main class="mpai-col mpai-center">
      <div id="mpai-uploads" class="mpai-uploads" data-state="idle">
        <div class="mpai-dropzone" id="mpai-dropzone">
          <div>
            <div class="muted" style="margin-bottom:6px;">Trascina qui file <b>sanificati</b> oppure</div>
            <label class="mpai-file-label">
              <input id="mpai-file-input" type="file" multiple style="display:none;">
              <span class="mpai-btn">📂 Aggiungi file</span>
            </label>
          </div>
        </div>
        <div id="mpai-file-list" class="mpai-filelist"></div>
      </div>
 
      <div id="mpai-chat" class="mpai-chat">
        <div class="mpai-msg mpai-hint">
          Benvenuto. Seleziona un progetto a sinistra, oppure scrivi subito: se non c’è un progetto
          creo una conversazione locale. Le risposte appariranno qui sotto in ordine, come in ChatGPT.
        </div>
      </div>


      <div class="mpai-composer">
        <textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea>
        <button id="mpai-send" class="mpai-btn primary">Invia</button>
      </div>
    </main>
    <!-- Sidebar destra: Impostazioni -->
    <aside class="mpai-col mpai-right">
      <h4>⚙️ Impostazioni</h4>
      <label class="mpai-field">Modello
        <select id="mpai-model">
          <option value="gpt-4o-2024-05-13" selected>gpt-4o-2024-05-13</option>
          <option value="gpt-4-turbo-2024-04-09">gpt-4-turbo-2024-04-09</option>
        </select>
      </label>
      <label class="mpai-field">Temperatura
        <input id="mpai-temp" type="number" min="0" max="1" step="0.1" value="0.7">
      </label>
      <label class="mpai-check">
        <input id="mpai-sanitized-only" type="checkbox" checked>
        Usa solo file <b>sanificati</b> come contesto
      </label>
      <div class="mpai-help muted">
        Le API key restano lato server (file sicuro). Il browser non vede mai le chiavi.
      </div>
    </aside>
  </div>
</div>


<!-- ========== SEZIONI LEGACY (restano, ma sono nascoste di default) ========== -->
<!-- ========== SEZIONI LEGACY (nascoste di default) ========== -->


<!-- 📦 Connessione API -->
<!-- 📦 Connessione API -->

Versione delle 15:52, 27 set 2025

🔧 Dashboard Operativa – Masticationpedia

Centro di comando per progetti, API, file e backup

🧾 Apri Log Dashboard