Dashboard Masticationpedia: differenze tra le versioni
Nessun oggetto della modifica |
Nessun oggetto della modifica |
||
| Riga 61: | Riga 61: | ||
</div> | </div> | ||
<div class="mpai-composer"> | |||
<textarea id="mpai-input" rows="3" placeholder="Scrivi qui la tua domanda..."></textarea> | |||
<div class="mpai-composer-right"> | |||
<label class="mpai-check" style="margin:0 0 6px 0;"> | |||
<input id="mpai-drop-urls" type="checkbox"> | |||
Ignora/Cancella URL e allegati per questo invio | |||
</label> | |||
<button id="mpai-send" class="mpai-btn primary">Invia</button> | |||
</div> | |||
</div> | </div> | ||
</main> | </main> | ||
| Riga 102: | Riga 102: | ||
.mpai-title{font-size:20px;font-weight:700} | .mpai-title{font-size:20px;font-weight:700} | ||
.mpai-actions{display:flex;gap:8px} | .mpai-actions{display:flex;gap:8px} | ||
.mpai-btn{border:1px solid var(--line);background: | .mpai-btn{border:1px solid var(--line);background:#fff;padding:.45rem .8rem;border-radius:8px;cursor:pointer} | ||
.mpai-btn:hover{background:#f3f4f6} | .mpai-btn:hover{background:#f3f4f6} | ||
.mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff} | .mpai-btn.primary{background:var(--primary);border-color:var(--primary);color:#fff} | ||
| Riga 109: | Riga 109: | ||
.muted{color:var(--muted)} | .muted{color:var(--muted)} | ||
.mpai-grid{display:grid;grid-template-columns:260px minmax(0,1fr) 260px;gap:12px} | .mpai-grid{display:grid;grid-template-columns:260px minmax(0,1fr) 260px;gap:12px} | ||
.mpai-col{background: | .mpai-col{background:#fff;border:1px solid var(--line);border-radius:12px;padding:12px} | ||
.mpai-left,.mpai-right{max-height:75vh;overflow:auto} | .mpai-left,.mpai-right{max-height:75vh;overflow:auto} | ||
.mpai-projects{display:flex;flex-direction:column;gap:6px;margin:8px 0} | .mpai-projects{display:flex;flex-direction:column;gap:6px;margin:8px 0} | ||
| Riga 135: | Riga 135: | ||
.mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px} | .mpai-composer textarea{flex:1 1 auto;min-height:90px;border:1px solid var(--line);border-radius:12px;padding:10px} | ||
.mpai-composer-right{display:flex;flex-direction:column;gap:6px} | .mpai-composer-right{display:flex;flex-direction:column;gap:6px} | ||
/* --- Blocchi codice nelle risposte + copia --- */ | |||
.mp-code{border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;margin:.5rem 0;background:#fff} | |||
.mp-code__hdr{display:flex;justify-content:space-between;align-items:center;padding:.5rem .75rem;border-bottom:1px solid #e5e7eb;background:#f8fafc} | |||
.mp-code__lang{font-family:ui-monospace, Menlo, Consolas, monospace;color:#334155} | |||
.mp-copy{border:1px solid #e5e7eb;background:#fff;border-radius:8px;padding:.25rem .5rem;cursor:pointer} | |||
.mp-copy:hover{background:#f1f5f9} | |||
.mp-code pre{margin:0;padding:.75rem 1rem;overflow:auto;font-family:ui-monospace, Menlo, Consolas, monospace;font-size:13px} | |||
@media (max-width:1100px){.mpai-grid{grid-template-columns:1fr}.mpai-left,.mpai-right{max-height:none}} | @media (max-width:1100px){.mpai-grid{grid-template-columns:1fr}.mpai-left,.mpai-right{max-height:none}} | ||
</style> | </style> | ||
<!-- ========= SEZIONI LEGACY (nascoste) ========= --> | |||
<div id="api-settings" style="display:none; padding:1rem; border:1px solid #ccc; border-radius:8px; background:#f9f9f9;"> | |||
<strong>Connessione API (protetta dal server)</strong><br><br> | |||
<label>Modello</label> | |||
<select id="model-select" style="width:100%; margin-bottom:0.5rem;"> | |||
<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>Prompt di test</label> | |||
<textarea id="test_prompt" rows="3" style="width:100%; margin-bottom:0.5rem;">Dimmi una curiosità sulla mandibola</textarea> | |||
< | <label style="display:inline-flex; gap:.4rem; align-items:center; margin:.25rem 0 .75rem;"> | ||
<input id="test_drop_urls" type="checkbox"> | |||
Ignora/Cancella URL e allegati per questo invio | |||
</label><br> | |||
<button id="test_run">Esegui</button> | |||
<pre id="api-result" style="background:#f0f0f0; padding:1rem; border:1px solid #ccc; margin-top:1rem; white-space:pre-wrap;"></pre> | |||
</div> | |||
</ | |||
< | <!-- ========== JS (copia codice, parser blocchi, invio) ========== --> | ||
<script> | |||
function toggleDashboardBox(id){ | |||
const el = document.getElementById(id); | |||
if (!el) return; | |||
el.style.display = (el.style.display === 'none' || !el.style.display) ? 'block' : 'none'; | |||
if (el.style.display === 'block') el.scrollIntoView({behavior:'smooth'}); | |||
} | |||
function showMpAI(){ document.getElementById('mpAI').style.display='block'; } | |||
function mpCopy(btn){ | |||
try{ | |||
const box = btn.closest('.mp-code'); | |||
const pre = box.querySelector('pre'); | |||
const txt = pre ? pre.innerText : ''; | |||
if (!txt) throw new Error('Codice non trovato'); | |||
navigator.clipboard.writeText(txt).then(()=>{ | |||
const old = btn.textContent; | |||
btn.textContent = '✅ Copiato'; | |||
setTimeout(()=>btn.textContent = old, 1200); | |||
}); | |||
}catch(e){ alert('Copia non riuscita: '+e.message); } | |||
} | |||
// Trasforma testo con blocchi ```lang\n...\n``` in nodi HTML con cornice + copia | |||
function renderTextWithCodeBlocks(text){ | |||
const frag = document.createDocumentFragment(); | |||
const re = /```(\w+)?\n([\s\S]*?)```/g; | |||
let lastIndex = 0, m; | |||
while ((m = re.exec(text)) !== null){ | |||
const before = text.slice(lastIndex, m.index).trim(); | |||
if (before){ | |||
const p = document.createElement('div'); | |||
p.textContent = before; | |||
frag.appendChild(p); | |||
} | |||
const lang = (m[1] || 'code').toLowerCase(); | |||
const code = m[2].replace(/\n+$/,''); | |||
const box = document.createElement('div'); box.className = 'mp-code'; | |||
const hdr = document.createElement('div'); hdr.className = 'mp-code__hdr'; | |||
hdr.innerHTML = `<span class="mp-code__lang">${lang}</span>`; | |||
const btn = document.createElement('button'); btn.className='mp-copy'; btn.textContent='📋 Copia'; btn.onclick=()=>mpCopy(btn); | |||
hdr.appendChild(btn); | |||
const pre = document.createElement('pre'); pre.textContent = code; | |||
box.appendChild(hdr); box.appendChild(pre); | |||
frag.appendChild(box); | |||
lastIndex = re.lastIndex; | |||
} | |||
const tail = text.slice(lastIndex).trim(); | |||
if (tail){ | |||
const p = document.createElement('div'); p.textContent = tail; | |||
frag.appendChild(p); | |||
} | |||
return frag; | |||
} | |||
(function(){ | |||
const $chat = document.getElementById('mpai-chat'); | |||
const $input = document.getElementById('mpai-input'); | |||
const $send = document.getElementById('mpai-send'); | |||
function addMsg(role, content){ | |||
const wrap = document.createElement('div'); | |||
wrap.className = 'mpai-msg ' + (role === 'user' ? 'user' : (role === 'assistant' ? 'assistant' : 'error')); | |||
const head = document.createElement('div'); head.className = 'role'; head.textContent = role; | |||
const body = document.createElement('div'); body.className = 'body'; | |||
if (role === 'assistant'){ | |||
body.appendChild(renderTextWithCodeBlocks(String(content || ''))); | |||
} else { | |||
body.textContent = String(content || ''); | |||
} | |||
wrap.appendChild(head); wrap.appendChild(body); | |||
$chat.appendChild(wrap); | |||
$chat.scrollTop = $chat.scrollHeight; | |||
} | |||
async function sendMessage(){ | |||
const prompt = ($input.value || '').trim(); | |||
if (!prompt) return; | |||
addMsg('user', prompt); | |||
$input.value = ''; | |||
const model = document.getElementById('mpai-model')?.value || 'gpt-4o-2024-05-13'; | |||
const temperature = parseFloat(document.getElementById('mpai-temp')?.value || '0.7'); | |||
const drop = document.getElementById('mpai-drop-urls')?.checked === true; | |||
const files = []; | |||
const texts = []; | |||
const payload = { model, prompt, temperature, drop_urls: drop, files, texts }; | |||
try { | |||
const res = await fetch('/dashboard/api/openai_project_gpt.php', { | |||
method: 'POST', | |||
headers: { 'Content-Type': 'application/json' }, | |||
credentials: 'include', | |||
body: JSON.stringify(payload) | |||
}); | |||
const ct = res.headers.get('content-type') || ''; | |||
let out; | |||
if (ct.includes('application/json')){ | |||
const j = await res.json(); | |||
out = j?.text ?? j?.message ?? (typeof j === 'string' ? j : JSON.stringify(j, null, 2)); | |||
} else { | |||
out = await res.text(); | |||
} | |||
addMsg('assistant', out || '(vuoto)'); | |||
} catch (e) { | |||
addMsg('error', String(e)); | |||
} | |||
} | |||
$send?.addEventListener('click', sendMessage); | |||
$input?.addEventListener('keydown', (e) => { | |||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) sendMessage(); | |||
}); | }); | ||
document.getElementById('mpai-clear')?.addEventListener('click', ()=>{ | |||
$chat.innerHTML = '<div class="mpai-msg mpai-hint">Conversazione pulita.</div>'; | |||
}); | |||
})(); | |||
} | |||
} | |||
// -------- pannello test “Connessione API” (force translate) ------- | |||
document.getElementById('test_run')?.addEventListener('click', async () => { | |||
const model = document.getElementById('model-select').value; | |||
const prompt = document.getElementById('test_prompt').value; | |||
const drop = document.getElementById('test_drop_urls').checked; | |||
const payload = { | |||
model, | |||
prompt, | |||
temperature: 0.7, | |||
drop_urls: drop, | |||
files: [], | |||
texts: [], | |||
mode: 'translate' // forza ramo traduzione per il test | |||
}; | |||
try { | |||
const res = await fetch('/dashboard/api/openai_project_gpt.php', { | |||
method: 'POST', | |||
headers: { 'Content-Type': 'application/json' }, | |||
credentials: 'include', | |||
body: JSON.stringify(payload) | |||
}); | |||
const j = await res.json(); | |||
document.getElementById('api-result').textContent = JSON.stringify(j, null, 2); | |||
} catch (e) { | |||
document.getElementById('api-result').textContent = String(e); | |||
} | |||
}); | |||
// Utility: svuota log lato server (se l’hai implementato) | |||
function clearServerLog(){ | |||
fetch('/dashboard/api/log_clear.php', {method:'POST', credentials:'include'}).then(()=>alert('Log svuotato')); | |||
} | |||
</script> | |||
</html> | </html> | ||
Versione delle 17:17, 12 ott 2025
🔧 Dashboard Operativa – Masticationpedia
Centro di comando per progetti, API, file e backup