Files
html/js/wevia-artifact-renderer.js
opus 420536a079
Some checks failed
WEVAL NonReg / nonreg (push) Has been cancelled
AUTO-BACKUP 20260421-1500
2026-04-21 15:00:04 +02:00

123 lines
7.9 KiB
JavaScript

// WEVIA Master Artifact Renderer - Wave Opus #1 - 2026-04-11
(function(){
function openArtifact(code, type, title){
type = (type||'html').toLowerCase();
title = title || 'Artifact';
var content = code;
if(type === 'mermaid'){
content = '<!DOCTYPE html><html><head><title>'+title+'</title>'
+'<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.0/mermaid.min.js"><\/script>'
+'<style>body{margin:20px;display:flex;align-items:center;justify-content:center;min-height:90vh;background:#0b0f1a;color:#e5e7eb;font-family:system-ui}.mermaid{background:#fff;padding:24px;border-radius:12px}</style>'
+'</head><body><div class="mermaid">'+code.replace(/</g,'&lt;')+'</div>'
+'<script>mermaid.initialize({startOnLoad:true,theme:"neutral"});<\/script></body></html>';
} else if(type === 'svg'){
content = '<!DOCTYPE html><html><head><title>'+title+'</title><style>body{margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#0b0f1a}</style></head><body>'+code+'</body></html>';
} else if(type === 'jsx' || type === 'react'){
content = '<!DOCTYPE html><html><head><title>'+title+'</title>'
+'<script src="https://unpkg.com/react@18/umd/react.production.min.js"><\/script>'
+'<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"><\/script>'
+'<script src="https://unpkg.com/@babel/standalone/babel.min.js"><\/script>'
+'<style>body{margin:0;padding:20px;background:#0b0f1a;color:#e5e7eb;font-family:system-ui;min-height:100vh}</style>'
+'</head><body><div id="root"></div><script type="text/babel">'+code+(code.indexOf('ReactDOM')===-1?'\nReactDOM.render(<App/>,document.getElementById("root"));':'')+'<\/script></body></html>';
} else if(type === 'html' && code.indexOf('<html')===-1){
content = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>'+title+'</title></head><body>'+code+'</body></html>';
}
var blob = new Blob([content], {type:'text/html'});
var url = URL.createObjectURL(blob);
window.open(url, '_blank', 'width=1200,height=800');
setTimeout(function(){URL.revokeObjectURL(url);}, 30000);
}
window.openArtifact = openArtifact;
// Auto-detect code blocks in Master responses
// INLINE_V2: build iframe content same way as openArtifact but embedded
function buildArtifactContent(code, type, title){
type = (type||'html').toLowerCase();
title = title || 'Artifact';
if(type === 'mermaid'){
return '<!DOCTYPE html><html><head><title>'+title+'</title>'
+'<script src="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.9.0/mermaid.min.js"><\/script>'
+'<style>body{margin:12px;display:flex;align-items:center;justify-content:center;min-height:90vh;background:#fff;font-family:system-ui}.mermaid{background:#fff;padding:12px;border-radius:8px}</style>'
+'</head><body><div class="mermaid">'+code.replace(/</g,'&lt;')+'</div>'
+'<script>mermaid.initialize({startOnLoad:true,theme:"neutral"});<\/script></body></html>';
} else if(type === 'svg'){
return '<!DOCTYPE html><html><head><title>'+title+'</title><style>body{margin:0;display:flex;align-items:center;justify-content:center;min-height:100vh;background:#fff}</style></head><body>'+code+'</body></html>';
} else if(type === 'jsx' || type === 'react'){
return '<!DOCTYPE html><html><head><title>'+title+'</title>'
+'<script src="https://unpkg.com/react@18/umd/react.production.min.js"><\/script>'
+'<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"><\/script>'
+'<script src="https://unpkg.com/@babel/standalone/babel.min.js"><\/script>'
+'<style>body{margin:0;padding:16px;background:#fff;font-family:system-ui;min-height:100vh}</style>'
+'</head><body><div id="root"></div><script type="text/babel">try{'+code+(code.indexOf('ReactDOM')===-1?'\nif(typeof App!=="undefined")ReactDOM.render(React.createElement(App),document.getElementById("root"));':'')+'}catch(e){document.getElementById("root").innerHTML="<pre style=\u0027color:#c00;padding:12px\u0027>Error: "+e.message+"</pre>";}<\/script></body></html>';
} else if(type === 'html' && code.indexOf('<html')===-1){
return '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>'+title+'</title></head><body>'+code+'</body></html>';
}
return code;
}
function scanAndAddButtons(){
document.querySelectorAll('pre code, pre').forEach(function(block){
if(block.dataset.artifactScanned) return;
block.dataset.artifactScanned = '1';
var txt = block.textContent || '';
var cls = block.className || (block.parentNode && block.parentNode.className) || '';
var type = null;
if(/language-mermaid|mermaid/i.test(cls) || /^graph\s|^flowchart\s|^sequenceDiagram|^pie\s|^gantt/m.test(txt)) type='mermaid';
else if(/language-(jsx|react)/i.test(cls) || /ReactDOM\.render|React\.use/.test(txt)) type='jsx';
else if(/language-html/i.test(cls) || /<!DOCTYPE|<html/i.test(txt)) type='html';
else if(/language-svg/i.test(cls) || /^<svg/i.test(txt.trim())) type='svg';
if(!type) return;
// Create wrapper with Preview button + inline iframe preview
var host = block.parentNode && block.parentNode.tagName === 'PRE' ? block.parentNode : block;
var insertBefore = host.nextSibling;
var container = host.parentNode;
if (!container) return;
// Button row
var row = document.createElement('div');
row.style.cssText = 'display:flex;gap:8px;margin:8px 0;align-items:center';
var btnInline = document.createElement('button');
btnInline.textContent = '▶ Aperçu inline';
btnInline.style.cssText='padding:6px 14px;background:linear-gradient(135deg,#7C6BF0,#22d3ee);color:#fff;border:0;border-radius:8px;cursor:pointer;font-weight:600;font-size:12px;box-shadow:0 2px 8px rgba(124,107,240,.3)';
var btnPopup = document.createElement('button');
btnPopup.textContent = '↗ Fenêtre';
btnPopup.style.cssText='padding:6px 14px;background:#22d3ee;color:#0b0f1a;border:0;border-radius:8px;cursor:pointer;font-weight:600;font-size:12px';
btnPopup.onclick = function(){ openArtifact(txt, type, 'WEVIA '+type); };
var label = document.createElement('span');
label.textContent = type.toUpperCase()+' · rendu live';
label.style.cssText='font-size:11px;color:#6b7280;margin-left:auto';
row.appendChild(btnInline); row.appendChild(btnPopup); row.appendChild(label);
container.insertBefore(row, insertBefore);
// Preview area (initially hidden)
var prev = document.createElement('div');
prev.className = 'artifact-preview';
prev.style.cssText = 'display:none;margin:8px 0;border-radius:12px;overflow:hidden;border:1px solid rgba(55,48,163,.15);background:#fff';
var iframe = document.createElement('iframe');
iframe.style.cssText = 'width:100%;height:350px;border:0';
iframe.sandbox = 'allow-scripts allow-same-origin';
prev.appendChild(iframe);
container.insertBefore(prev, insertBefore);
btnInline.onclick = function(){
if (prev.style.display === 'none') {
var content = buildArtifactContent(txt, type, 'WEVIA '+type);
var blob = new Blob([content], {type:'text/html'});
iframe.src = URL.createObjectURL(blob);
prev.style.display = 'block';
btnInline.textContent = '▼ Masquer aperçu';
} else {
prev.style.display = 'none';
btnInline.textContent = '▶ Aperçu inline';
}
};
});
}
var mo = new MutationObserver(function(){ setTimeout(scanAndAddButtons, 100); });
document.addEventListener('DOMContentLoaded', function(){
scanAndAddButtons();
mo.observe(document.body, {childList:true, subtree:true});
});
if(document.readyState !== 'loading'){
scanAndAddButtons();
mo.observe(document.body, {childList:true, subtree:true});
}
})();