debut illustration

This commit is contained in:
pporcheret
2026-11-02 08:50:05 +01:00
parent e2340807aa
commit c2a4f7eb26
44 changed files with 6856 additions and 19 deletions
+60 -12
View File
@@ -10,6 +10,27 @@
};
const btn=(text,fn,cls='d8-btn')=>{const b=el('button',cls,text);b.type='button';b.addEventListener('click',fn);return b;};
let active=null,opened=false,busy=false,ticker=null;
// DONA_DYNAMIC_PERF_V2 — HTTP 202 is queue acceptance, not generation completion.
let creating=false, createTimer=null;
const CREATE_MAX_WAIT_MS=240000;
function updateCreationControls(){
const root=document.getElementById(ID);if(!root)return;
root.querySelectorAll('.d8-submit, .d8-examples button').forEach(button=>{
button.disabled=creating;button.setAttribute('aria-disabled',String(creating));
});
}
function finishCreating(){
if(createTimer!==null){clearTimeout(createTimer);createTimer=null;}
creating=false;updateCreationControls();
}
function startCreating(){
creating=true;updateCreationControls();
if(createTimer!==null)clearTimeout(createTimer);
createTimer=setTimeout(()=>{
finishCreating();
if(opened)status('Confirmation non reçue. Tu peux réessayer ; une génération peut encore être en cours côté serveur.');
},CREATE_MAX_WAIT_MS);
}
const KEY='dona.dynamic.pins.v8';
const safeGetPins=()=>{
try{const pins=JSON.parse(localStorage.getItem(KEY)||'[]');return Array.isArray(pins)?pins.slice(0,8):[];}
@@ -50,10 +71,10 @@
function open(){opened=true;$(ID).hidden=false;document.body.classList.add('d8-open');$('d8Input').focus();renderPins();schedule();}
function close(){opened=false;$(ID).hidden=true;document.body.classList.remove('d8-open');stopTimer();}
function stopTimer(){if(ticker){clearInterval(ticker);ticker=null;}}
function schedule(){stopTimer();if(!opened||!active||!['spotify','homeassistant','lights'].includes(active.origin))return;
function schedule(){stopTimer();if(!opened||!active||!['spotify','homeassistant','lights','proxmox'].includes(active.origin))return;
ticker=setInterval(()=>{if(!document.hidden&&!busy)reloadLive(false);},active.origin==='spotify'?15000:30000);
}
function originLabel(origin){return ({spotify:'Spotify · direct',homeassistant:'Home Assistant · direct',lights:'Home Assistant · direct',hermes:'Hermes · MCP',fallback:'Hermes · réponse texte'})[origin]||'Données contextuelles';}
function originLabel(origin){return ({spotify:'Spotify · direct',homeassistant:'Home Assistant · direct',lights:'Home Assistant · direct',hermes:'Hermes · MCP',fallback:'Hermes · réponse texte',proxmox:'Proxmox · API directe' /* DONA_DYNAMIC_PROXMOX_V3 */})[origin]||'Données contextuelles';}
const tagText=(parent,tag,cls,text)=>{parent.append(el(tag,cls,text));};
function drawCard(c){
const section=el('article','d8-card d8-'+c.type);
@@ -128,18 +149,30 @@
const pins=safeGetPins();pins.unshift(active);safeSavePins(pins);renderPins();status('Tableau épinglé dans ce navigateur.');}
async function post(url,body){
const res=await fetch(url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-Alfred-Request':'1'},body:JSON.stringify(body)});
const obj=await res.json().catch(()=>({}));if(!res.ok||obj.ok===false)throw Error(obj.error||'HTTP '+res.status);return obj;
const obj=await res.json().catch(()=>({}));if(!res.ok||obj.ok===false){const error=Error(obj.error||'HTTP '+res.status);error.httpStatus=res.status;throw error;}return obj;
}
async function create(prompt){
const query=String(prompt||'').trim();if(query.length<2||query.length>500){status('Décris le tableau en 2 à 500 caractères.');return;}
if(busy)return;busy=true;status('Dona prépare le tableau…');
try{await post('/api/dona/dynamic/create',{prompt:query});status('Dona consulte les données ; la vue apparaîtra ici.');}
catch(e){status(e.message||'Demande impossible.');}
finally{busy=false;}
if(creating){status('Une génération est déjà en cours.');return;}
if(busy){status('Une actualisation est en cours.');return;}
startCreating();status('Dona prépare le tableau…');
try{
await post('/api/dona/dynamic/create',{prompt:query});
// A fast Socket.IO completion may have already cleared creating.
if(creating)status('Dona consulte les données ; la vue apparaîtra ici.');
}catch(e){
if(e.httpStatus===409){
// Another tab or another entry point may have submitted the job.
// Wait for its completion event, or release after the safety timeout.
status('Une génération est déjà en cours côté serveur. Attente du résultat…');
}else{
finishCreating();status(e.message||'Demande impossible.');
}
}
}
async function reloadLive(notify=true){
if(!active)return;
if(['spotify','homeassistant','lights'].includes(active.origin)){
if(['spotify','homeassistant','lights','proxmox'].includes(active.origin)){
if(busy)return;const current=active;busy=true;
try{const r=await fetch('/api/dona/dynamic/live?source='+encodeURIComponent(active.origin),{cache:'no-store',credentials:'same-origin'});
const j=await r.json();if(!r.ok||!j.ok)throw Error(j.error||'Source indisponible');
@@ -163,14 +196,29 @@
primary.prepend(b);
}
let tries=0;const timer=setInterval(()=>{initButton();if($('d8Open')||++tries>70)clearInterval(timer);},150);
// DONA_UNIFIED_ROUTING_V4 — conserver le tableau quand Dona confirme à voix haute.
let v4IgnoreNextTextUntil=0;
const socket=window.alfredSocket;
socket?.on('alfred_render',data=>{
if(data?.type==='dona_dynamic')render(data.dashboard);
else if(opened && data?.type!=='dona_dynamic')close();
if(data?.type==='dona_dynamic' && data.dona_fast_v4===true)
v4IgnoreNextTextUntil=Date.now()+15000;
if(data?.type==='dona_dynamic'){
// Only a server result (not a pinned board or HTTP 202) ends the wait.
if(data.dashboard?.schema==='dona.dashboard.v1'&&Array.isArray(data.dashboard.cards))finishCreating();
render(data.dashboard);
}
else if(opened && data?.type!=='dona_dynamic'){
if(v4IgnoreNextTextUntil && Date.now()<v4IgnoreNextTextUntil){
v4IgnoreNextTextUntil=0; // Ignorer UNE confirmation de la commande directe.
}else{v4IgnoreNextTextUntil=0;close();}
}
});
socket?.on('alfred_state',data=>{
if(opened && data?.mode==='error')status('Erreur lors de la préparation du tableau.');
if(opened && data?.mode==='error'){
if(creating)finishCreating();
status('Erreur lors de la préparation du tableau.');
}
});
document.addEventListener('visibilitychange',()=>{if(!document.hidden&&opened&&active&&['spotify','homeassistant','lights'].includes(active.origin))reloadLive(false);});
document.addEventListener('visibilitychange',()=>{if(!document.hidden&&opened&&active&&['spotify','homeassistant','lights','proxmox'].includes(active.origin))reloadLive(false);});
window.DonaDynamicV8={open,render,close};
})();