/* DONA_UI_V92 — Hamburger + raccourcis éditables, sans dupliquer les contrôleurs. */ (()=>{ 'use strict'; if(window.DonaUIV92)return; const $=id=>document.getElementById(id); const create=(tag,cls,text)=>{const el=document.createElement(tag);if(cls)el.className=cls;if(text!==undefined)el.textContent=String(text);return el;}; const button=(text,fn,cls='dv92-btn')=>{const b=create('button',cls,text);b.type='button';b.addEventListener('click',fn);return b;}; const icons={clock:'◷','cloud-sun':'☀','house':'⌂',server:'▤','shield-check':'⬡',image:'▧','chef-hat':'♨','calendar-days':'▦',sun:'☼',lightbulb:'♧','list-checks':'☷',music:'♫',youtube:'▶',map:'⌖',camera:'▣','book-open':'▤',sparkles:'✦',brain:'✧',settings:'⚙',bell:'♧',zap:'ϟ',star:'☆','file-text':'▤',terminal:'⌘'}; const labels={clock:'Horloge','cloud-sun':'Météo',house:'Maison',server:'Serveur','shield-check':'Sécurité',image:'Image','chef-hat':'Cuisine','calendar-days':'Agenda',sun:'Soleil',lightbulb:'Lumière','list-checks':'Liste',music:'Musique',youtube:'YouTube',map:'Carte',camera:'Caméra','book-open':'Livre',sparkles:'Étoile',brain:'IA',settings:'Réglages',bell:'Notification',zap:'Éclair',star:'Favori','file-text':'Document',terminal:'Terminal'}; const builtins=new Set(['time','weather','house','proxmox','wazuh','comfyui']); const builtinTitles={time:'Quelle heure est-il ?',weather:'Météo',house:'État de la maison',proxmox:'Vérifier Proxmox',wazuh:'Alertes Wazuh',comfyui:'Créer une image'}; const fallback=['time','weather','house','proxmox','wazuh','comfyui'].map((id,i)=>({id,title:builtinTitles[id],icon:['clock','cloud-sun','house','server','shield-check','image'][i],prompt:builtinTitles[id],visible:true,mode:'native',action:id})); let items=fallback.map(x=>({...x}));let editing=false,draft=[];let sending=false; let header,bar,menu,burger,editDialog,tray,shortcutHost,status; function msg(text){if(status)status.textContent=String(text||'');} function request(url,body){return fetch(url,{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-Alfred-Request':'1'},body:JSON.stringify(body)}).then(async r=>{let data=await r.json().catch(()=>({}));if(!r.ok||data.ok===false)throw Error(data.error||'Erreur '+r.status);return data;});} function icon(key){return create('span','dv92-symbol',icons[key]||'✦');} function closeMenu(){if(!menu)return;menu.hidden=true;burger?.setAttribute('aria-expanded','false');} function positionMenu(){if(!menu||menu.hidden)return;const rect=burger.getBoundingClientRect();let left=Math.min(rect.right-menu.offsetWidth,window.innerWidth-menu.offsetWidth-8);left=Math.max(8,left);let top=rect.bottom+9;if(top+menu.offsetHeight>window.innerHeight-8)top=Math.max(8,rect.top-menu.offsetHeight-9);menu.style.left=left+'px';menu.style.top=top+'px';} function showMenu(){menu.hidden=!menu.hidden;burger.setAttribute('aria-expanded',String(!menu.hidden));if(!menu.hidden){renderMenu();positionMenu();}} function renderMenu(){ menu.replaceChildren(create('div','dv92-menu-heading','Outils Dona')); const controls=Array.from(bar.children).filter(x=>x!==burger&&x.id!=='avTopMic'); for(const original of controls){ const title=(original.getAttribute('aria-label')||original.title||original.textContent||'Outil').trim(); const proxy=button('',()=>{closeMenu();original.click();},'dv92-menu-entry'); const ico=original.querySelector('svg'); if(ico){const svg=ico.cloneNode(true);svg.removeAttribute('id');svg.setAttribute('aria-hidden','true');proxy.append(svg);} else proxy.append(create('span','dv92-menu-symbol',(original.textContent||'•').trim().slice(0,2))); proxy.append(create('span','',title));menu.append(proxy); } menu.append(create('div','dv92-menu-rule')); menu.append(button('✎ Modifier les raccourcis',()=>{closeMenu();openEditor();},'dv92-menu-entry dv92-menu-edit')); } function initHeader(root){ bar=root.querySelector('.av-head-actions');if(!bar)return; burger=button('☰',showMenu,'av-round dv92-hamburger');burger.id='dv92Hamburger';burger.setAttribute('aria-label','Ouvrir le menu des outils');burger.setAttribute('aria-expanded','false');burger.setAttribute('aria-controls','dv92ToolsMenu');burger.title='Menu des outils'; bar.append(burger); menu=create('div','dv92-menu');menu.id='dv92ToolsMenu';menu.hidden=true;menu.setAttribute('role','group');menu.setAttribute('aria-label','Outils Dona');document.body.append(menu); renderMenu(); const observer=new MutationObserver(()=>{if(!menu.hidden){renderMenu();positionMenu();}});observer.observe(bar,{childList:true}); window.addEventListener('resize',positionMenu,{passive:true});window.addEventListener('scroll',positionMenu,{passive:true}); document.addEventListener('pointerdown',e=>{if(!menu.hidden&&!menu.contains(e.target)&&!burger.contains(e.target))closeMenu();}); document.addEventListener('keydown',e=>{if(e.key==='Escape'){closeMenu();closeEditor();}}); } async function submitPrompt(prompt){ if(sending)return;sending=true;msg('Transmission à Dona…'); try{const x=await request('/api/dona/texte',{message:prompt});msg(x.message||'Demande transmise à Dona.');} catch(e){msg('Erreur : '+e.message);} finally{sending=false;} } function drawShortcuts(){ if(!shortcutHost)return; shortcutHost.replaceChildren(); for(const item of items.filter(x=>x.visible)){ const b=button('',()=>{},'dv92-shortcut');b.replaceChildren(icon(item.icon),create('span','',item.title));b.title=item.mode==='prompt'?item.prompt:item.title; if(item.mode==='native'&&builtins.has(item.id)){ b.dataset.avAction=item.action; // The original delegated handler on #alfredMainV1 handles native actions. }else{ b.addEventListener('click',e=>{e.stopPropagation();submitPrompt(item.prompt);}); } shortcutHost.append(b); } if(!shortcutHost.children.length)shortcutHost.append(create('p','dv92-shortcut-empty','Aucun raccourci visible. Clique sur Modifier pour en ajouter.')); } function editorButton(){ tray=shortcutHost.closest('.dona-tools-tray');if(!tray)return; const tools=create('div','dv92-shortcut-toolbar');tools.append(button('✎ Modifier les raccourcis',openEditor,'dv92-edit-open'));status=create('span','dv92-shortcut-status');status.setAttribute('role','status');status.setAttribute('aria-live','polite');tools.append(status); shortcutHost.before(tools); } function openEditor(){ if(!editDialog)return; draft=items.map(x=>({...x}));editing=true;drawEditor();editDialog.hidden=false;document.body.classList.add('dv92-editing'); } function closeEditor(){if(!editDialog||!editing)return;editing=false;editDialog.hidden=true;document.body.classList.remove('dv92-editing');} function editorFields(item,index,host){ const summary=create('summary','dv92-item-summary');summary.append(icon(item.icon),create('strong','dv92-summary-name',item.title),create('span','dv92-summary-mode',item.mode==='native'?'Action intégrée':'Texte personnalisé')); const detail=create('details','dv92-edit-item');detail.append(summary); const controls=create('div','dv92-inline-controls'); const up=button('↑',()=>move(index,-1),'dv92-small');up.title='Monter';up.disabled=index===0; const down=button('↓',()=>move(index,1),'dv92-small');down.title='Descendre';down.disabled=index===draft.length-1; const visible=create('label','dv92-check');const check=create('input');check.type='checkbox';check.checked=item.visible;check.addEventListener('change',()=>{item.visible=check.checked;});visible.append(check,create('span','', 'Afficher')); const del=button('Supprimer',()=>{if(confirm('Supprimer ce raccourci ?')){draft.splice(index,1);drawEditor();}},'dv92-small dv92-danger'); controls.append(visible,up,down,del);detail.append(controls); function field(labelText,control){const box=create('label','dv92-field');box.append(create('span','',labelText),control);return box;} const title=create('input','dv92-input');title.type='text';title.maxLength=70;title.value=item.title;title.required=true;title.addEventListener('input',()=>{item.title=title.value;summary.querySelector('.dv92-summary-name').textContent=title.value;}); detail.append(field('Nom affiché',title)); const pick=create('select','dv92-input');for(const [key,label] of Object.entries(labels)){const o=create('option','',`${icons[key]} ${label}`);o.value=key;o.selected=key===item.icon;pick.append(o);}pick.addEventListener('change',()=>{item.icon=pick.value;summary.querySelector('.dv92-symbol').textContent=icons[pick.value];}); detail.append(field('Icône',pick)); const how=create('select','dv92-input');const nativeOption=create('option','', 'Action intégrée (fonctionnement d’origine)');nativeOption.value='native';const promptOption=create('option','', 'Envoyer mon texte à Dona / Hermes');promptOption.value='prompt';if(builtins.has(item.id))how.append(nativeOption);how.append(promptOption);how.value=item.mode; how.addEventListener('change',()=>{item.mode=how.value;summary.querySelector('.dv92-summary-mode').textContent=how.value==='native'?'Action intégrée':'Texte personnalisé';}); detail.append(field('Quand je touche ce raccourci',how)); const prompt=create('textarea','dv92-textarea');prompt.rows=3;prompt.maxLength=1600;prompt.value=item.prompt;prompt.addEventListener('input',()=>{item.prompt=prompt.value;if(item.mode==='native'){item.mode='prompt';how.value='prompt';summary.querySelector('.dv92-summary-mode').textContent='Texte personnalisé';}}); detail.append(field('Texte exact envoyé à Dona',prompt)); detail.append(create('p','dv92-help', 'Modifier ce texte active automatiquement le mode « Texte personnalisé ». Les commandes passent par le formulaire écrit de Dona, sans synthèse vocale.')); host.append(detail); } function move(i,d){const j=i+d;if(j<0||j>=draft.length)return;[draft[i],draft[j]]=[draft[j],draft[i]];drawEditor();} function drawEditor(){const target=$('dv92EditorRows');if(!target)return;target.replaceChildren();draft.forEach((item,i)=>editorFields(item,i,target));$('dv92EditorCount').textContent=draft.length+' / 35';} async function saveEditor(){ const save=$('dv92Save');save.disabled=true; try{const data=await request('/api/dona/v92/shortcuts',{items:draft});items=data.items;drawShortcuts();closeEditor();msg('Raccourcis enregistrés.');} catch(e){$('dv92EditorStatus').textContent='Erreur : '+e.message;} finally{save.disabled=false;} } async function resetEditor(){ if(!confirm('Restaurer les raccourcis d’origine ? Tes modifications seront perdues.'))return; try{const data=await request('/api/dona/v92/shortcuts/reset',{});items=data.items;draft=items.map(x=>({...x}));drawEditor();drawShortcuts();$('dv92EditorStatus').textContent='Raccourcis réinitialisés.';} catch(e){$('dv92EditorStatus').textContent='Erreur : '+e.message;} } function addShortcut(){ if(draft.length>=35){$('dv92EditorStatus').textContent='Maximum de 35 raccourcis.';return;} const uniq=()=>('user_'+Math.random().toString(36).slice(2,12)+Date.now().toString(36)); draft.push({id:uniq(),title:'Nouveau raccourci',icon:'sparkles',prompt:'Dona, ',visible:true,mode:'prompt',action:''});drawEditor(); const last=$('dv92EditorRows').lastElementChild;last.open=true;last.querySelector('.dv92-input')?.focus(); } function initEditor(){ editDialog=create('div','dv92-editor-overlay');editDialog.id='dv92Editor';editDialog.hidden=true; const panel=create('section','dv92-editor');panel.setAttribute('role','dialog');panel.setAttribute('aria-modal','true');panel.setAttribute('aria-label','Modifier les raccourcis Dona'); const heading=create('div','dv92-editor-header');const title=create('div');title.append(create('small','', 'DONA · PERSONNALISATION'),create('h2','', 'Mes raccourcis')); heading.append(title,button('×',closeEditor,'dv92-close'));panel.append(heading); panel.append(create('p','dv92-help','Renomme, classe ou masque tes raccourcis. Modifie « Texte exact envoyé à Dona » pour personnaliser leur commande.')); const counter=create('div','dv92-count');counter.id='dv92EditorCount';panel.append(counter); const list=create('div','dv92-editor-list');list.id='dv92EditorRows';panel.append(list); const bottom=create('div','dv92-editor-actions');bottom.append(button('+ Ajouter un raccourci',addShortcut,'dv92-btn dv92-secondary'),button('↺ Réinitialiser',resetEditor,'dv92-btn dv92-secondary'),button('Annuler',closeEditor,'dv92-btn dv92-secondary')); const save=button('Enregistrer',saveEditor,'dv92-btn dv92-primary');save.id='dv92Save';bottom.append(save);panel.append(bottom); const message=create('div','dv92-feedback');message.id='dv92EditorStatus';message.setAttribute('role','status');panel.append(message);editDialog.append(panel);document.body.append(editDialog); editDialog.addEventListener('pointerdown',e=>{if(e.target===editDialog)closeEditor();}); } async function loadShortcuts(){ try{const response=await fetch('/api/dona/v92/shortcuts',{credentials:'same-origin',cache:'no-store'});const data=await response.json();if(!response.ok||!data.ok)throw Error(data.error||'Erreur de lecture');items=data.items;drawShortcuts();} catch(e){drawShortcuts();msg('Raccourcis par défaut. Configuration indisponible : '+e.message);} } function init(){ const root=$('alfredMainV1');if(!root||!root.querySelector('.av-head-actions')||!root.querySelector('.av-shortcuts'))return false; shortcutHost=root.querySelector('.av-shortcuts'); initHeader(root);editorButton();initEditor();loadShortcuts(); window.DonaUIV92={openEditor};return true; } let tries=0;function ready(){if(!init()&&tries++<80)setTimeout(ready,150);} if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',ready,{once:true});else ready(); })();