144 lines
13 KiB
JavaScript
144 lines
13 KiB
JavaScript
/* 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();
|
||
})();
|