Nota: dopo aver pubblicato, potrebbe essere necessario pulire la cache del proprio browser per vedere i cambiamenti.
- Firefox / Safari: tieni premuto il tasto delle maiuscole Shift e fai clic su Ricarica, oppure premi Ctrl-F5 o Ctrl-R (⌘-R su Mac)
- Google Chrome: premi Ctrl-Shift-R (⌘-Shift-R su un Mac)
- Edge: tieni premuto il tasto Ctrl e fai clic su Aggiorna, oppure premi Ctrl-F5.
/* =========================================================================
CommonDashboard.js — STABILE SEMPLICE (ripristino sicuro)
- NON nasconde elementi esistenti
- Ripristina pulsante "Importa Wikitesto da URL"
- Risposte sempre mostrate anche se il server manda text/plain
- Renderer Markdown + Highlight "leggero"
========================================================================= */
/* Guardia anti doppio-caricamento */
if (window.__MP_DASHBOARD_SAFE__) {
console.warn('CommonDashboard.js già caricato.');
} else {
window.__MP_DASHBOARD_SAFE__ = true;
(function () {
console.log('🧭 CommonDashboard SAFE: init');
/* ========== Loader minimale per marked + highlight ========== */
function loadScript(src) {
return new Promise((ok, no) => {
const s = document.createElement('script'); s.src = src; s.onload = ok; s.onerror = no;
document.head.appendChild(s);
});
}
function loadStyle(href) {
return new Promise((ok, no) => {
const l = document.createElement('link'); l.rel = 'stylesheet'; l.href = href; l.onload = ok; l.onerror = no;
document.head.appendChild(l);
});
}
async function ensureRenderer() {
if (!window.marked) {
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.2/marked.min.js');
}
if (!window.hljs) {
await loadStyle('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css');
await loadScript('https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js');
}
}
function renderMarkdownInto(el, md) {
if (!el) return;
if (window.marked) {
el.innerHTML = window.marked.parse(String(md || ''));
if (window.hljs) {
el.querySelectorAll('pre code').forEach(c => { try { window.hljs.highlightElement(c); } catch {} });
}
} else {
el.textContent = String(md || '');
}
}
/* ========== Helper UI di chat, non nasconde nulla della tua pagina ========== */
function $(sel) { return document.querySelector(sel); }
function appendMsg(role, content) {
const host = $('#mpai-chat'); if (!host) return;
const box = document.createElement('div');
box.className = 'mpai-msg ' + (role === 'assistant' ? 'assistant' : role === 'user' ? 'user' : 'error');
box.innerHTML = `
<div class="role">${role === 'assistant' ? 'GPT' : role === 'user' ? 'Tu' : 'Errore'}</div>
<div class="content"><em>…</em></div>
<div class="meta"></div>`;
host.appendChild(box);
host.scrollTop = host.scrollHeight;
const c = box.querySelector('.content');
renderMarkdownInto(c, String(content || ''));
}
function showTyping() {
const host = $('#mpai-chat'); if (!host) return null;
const box = document.createElement('div');
box.className = 'mpai-msg assistant';
box.innerHTML = `<div class="role">GPT</div><div class="content">…</div>`;
host.appendChild(box);
host.scrollTop = host.scrollHeight;
return box;
}
function hideTyping(el) { try { el && el.remove(); } catch {} }
/* ========== INVIO PROMPT — semplice e robusto ========== */
window.sendPrompt = async function () {
const ta = $('#mpai-input'); if (!ta) return;
const content = (ta.value || '').trim();
if (!content) { ta.focus(); return; }
appendMsg('user', content);
ta.value = '';
const typing = showTyping();
// Legge modello dall’UI se presente
const modelSel = $('#mpai-model');
const model = modelSel ? (modelSel.value || 'gpt-4o-2024-05-13') : 'gpt-4o-2024-05-13';
// PAYLOAD minimale (non tocco la tua dropzone/filelist esistente)
const payload = { model, prompt: content, temperature: 0.7 };
try {
const res = await fetch('/dashboard/api/openai_project_gpt.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
cache: 'no-store',
body: JSON.stringify(payload)
});
const raw = await res.text();
hideTyping(typing);
// Mostra sempre qualcosa anche se è text/plain
let out = '';
try {
const j = JSON.parse(raw);
out = j.result || j.text || j.reply || j.message || j.output_text || JSON.stringify(j, null, 2);
} catch {
out = raw;
}
out = String(out || '').replace(/\\n/g, '\n');
if (!res.ok) {
appendMsg('error', `HTTP ${res.status} ${res.statusText}\n\n${out}`);
} else {
appendMsg('assistant', out || '[nessun testo]');
}
} catch (e) {
hideTyping(typing);
appendMsg('error', 'Errore rete/JS: ' + (e && e.message ? e.message : e));
}
};
/* ========== BIND UI: non nascondo la tua dropzone né la lista file ========== */
function bindUI() {
const sendBtn = $('#mpai-send');
if (sendBtn && !sendBtn.__bound) {
sendBtn.__bound = true;
sendBtn.addEventListener('click', window.sendPrompt);
}
// Invio con Cmd/Ctrl+Enter
document.addEventListener('keydown', (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
const ta = $('#mpai-input');
if (ta && document.activeElement === ta) { e.preventDefault(); window.sendPrompt(); }
}
});
console.log('🔗 SAFE bind ok');
}
/* ========== RIPRISTINO: Pulsante "Importa Wikitesto da URL" ========== */
function ensureImportButton() {
// NON lo nascondo più in nessun caso
// Lo aggancio vicino al titolo "Masticationpedia AI" se esiste, altrimenti in alto nella pagina
const target =
Array.from(document.querySelectorAll('h2,h3,.section-title,.mpai-header,.mp-section-title'))
.find(n => /Masticationpedia\s*AI/i.test(n.textContent || '')) ||
document.querySelector('#contentSub') ||
document.body;
if (!target || document.getElementById('mp-btn-import-url')) return;
const btn = document.createElement('button');
btn.id = 'mp-btn-import-url';
btn.textContent = 'Importa Wikitesto da URL';
btn.title = 'Scarica una pagina MediaWiki e salvala tra i file';
btn.style.cssText = 'margin-left:.5rem;padding:.35rem .7rem;border:1px solid #444;border-radius:10px;cursor:pointer;font-size:.9rem;';
target.appendChild(btn);
btn.addEventListener('click', () => openImportModal());
console.log('✅ Pulsante "Importa Wikitesto da URL" ripristinato');
}
function openImportModal() {
const modal = document.createElement('div');
modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99999;display:flex;align-items:center;justify-content:center;';
modal.innerHTML = `
<div style="background:#fff;max-width:720px;width:92%;padding:1rem;border-radius:12px;box-shadow:0 8px 30px rgba(0,0,0,.15);">
<h3 style="margin:.2rem 0 1rem;">Importa Wikitesto da URL</h3>
<label style="display:block;margin:.4rem 0 .2rem;">URL della pagina (MediaWiki / Wikipedia / GitHub raw):</label>
<input id="mp-url" type="url" placeholder="https://staging.masticationpedia.org/wiki/Introduction" style="width:100%;padding:.6rem;border:1px solid #ccc;border-radius:8px;">
<div id="mp-status" style="margin-top:.6rem;font-size:.9rem;opacity:.85;"></div>
<div style="margin-top:1rem;display:flex;justify-content:flex-end;gap:.6rem;">
<button id="mp-cancel" style="padding:.5rem 1rem;border:1px solid #666;border-radius:10px;background:#fff;cursor:pointer;">Annulla</button>
<button id="mp-go" style="padding:.5rem 1rem;border:1px solid #333;border-radius:10px;background:#111;color:#fff;cursor:pointer;">Importa</button>
</div>
</div>`;
document.body.appendChild(modal);
const elUrl = modal.querySelector('#mp-url');
const elStatus = modal.querySelector('#mp-status');
modal.querySelector('#mp-cancel').onclick = () => modal.remove();
modal.querySelector('#mp-go').onclick = async () => {
const url = (elUrl.value || '').trim();
if (!url) { elStatus.textContent = 'Inserisci una URL valida.'; elStatus.style.color = '#b00'; return; }
elStatus.textContent = 'Import in corso...'; elStatus.style.color = '';
try {
const form = new FormData();
form.append('url', url);
form.append('save', '1'); // server salverà nel progetto di default
const res = await fetch('/dashboard/api/fetch_wikitext.php', { method: 'POST', body: form, credentials: 'include' });
const raw = await res.text();
if (!res.ok) throw new Error('HTTP ' + res.status + ' ' + res.statusText + ' — ' + raw);
let j = {};
try { j = JSON.parse(raw); } catch { j = { ok:false, raw }; }
if (!j.ok) throw new Error(j.error || 'Errore server');
elStatus.innerHTML = '✅ Import riuscito: <code>' + j.relative + '</code> (' + j.bytes + ' bytes)<br><small>Origine: ' + j.resolved_url + '</small>';
elStatus.style.color = '#0a0';
setTimeout(() => modal.remove(), 1200);
} catch (e) {
elStatus.textContent = 'Errore: ' + (e && e.message ? e.message : e);
elStatus.style.color = '#b00';
}
};
}
/* ========== AVVIO ========== */
function boot() {
ensureRenderer().then(() => {
bindUI();
ensureImportButton();
console.log('✅ CommonDashboard SAFE: pronta');
}).catch((e) => {
console.warn('Renderer non caricato:', e);
bindUI();
ensureImportButton();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else { boot(); }
})();
}