123 lines
7.9 KiB
JavaScript
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,'<')+'</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,'<')+'</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});
|
|
}
|
|
})();
|