Files
Alfredv2/alfred/app/static/js/dona-ui-v9-2.js
T
2026-10-31 11:04:45 +01:00

144 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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();
})();