v4
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"kind": "dona-avatar-v1",
|
||||
"app": "/opt/alfred/app",
|
||||
"original_files": [
|
||||
"templates/index.html",
|
||||
"static/js/dona-accueil-ecrit-v1.js"
|
||||
],
|
||||
"created_files": [
|
||||
"static/css/dona-avatar-v1.css",
|
||||
"static/js/dona-avatar-v1.js",
|
||||
"static/img/dona-avatar-portrait.png"
|
||||
],
|
||||
"installed_sha256": {
|
||||
"templates/index.html": "eb039ad650db5f3a735baf702749a555c539c577020c77d9d3de89168dc3ade2",
|
||||
"static/js/dona-accueil-ecrit-v1.js": "5f85b649d20270a1be55ca4dbbab30918e1d0ea792052d880613a5a76db7f834",
|
||||
"static/css/dona-avatar-v1.css": "e79daa023147bcca69a3bba2f06927b79e3d1426fc24c57a9daff7314dc95d9c",
|
||||
"static/js/dona-avatar-v1.js": "2399c2679693abddbc8a483bb5e6b14bc5119e0ef38222ea7d684b8fcbc31d57",
|
||||
"static/img/dona-avatar-portrait.png": "ffaba09cd4528f48d93ab1aadf5e662d44872563ccf657961d08f681afee80b8"
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/* DONA_FAMILY_UI_V1: non-destructive augmentation of the installed Glass V1 dashboard. */
|
||||
(() => {
|
||||
'use strict';
|
||||
let attempts=0;
|
||||
function init(){
|
||||
const root=document.getElementById('alfredMainV1');
|
||||
if(!root){if(++attempts<40)setTimeout(init,150);return;}
|
||||
if(root.dataset.donaFamily==='ready')return;
|
||||
const $=id=>document.getElementById(id);
|
||||
const head=root.querySelector('.av-header-right');
|
||||
const weather=root.querySelector('.av-weather');
|
||||
const hero=root.querySelector('.av-voice-hero');
|
||||
const shortcuts=root.querySelector('.av-shortcuts');
|
||||
if(!head||!weather||!hero||!shortcuts)return;
|
||||
root.dataset.donaFamily='ready';document.body.classList.add('dona-family-v1');
|
||||
|
||||
// Move the EXISTING weather widget: old JS keeps updating the SAME IDs.
|
||||
weather.classList.add('dona-inline-weather');
|
||||
weather.setAttribute('title','Ouvrir les détails météo');
|
||||
head.prepend(weather);
|
||||
|
||||
// Child link is supplementary; all old admin/navigation functions remain.
|
||||
const kid=document.createElement('a');kid.href='/enfant';kid.className='dona-child-link';kid.textContent='⭐ Espace enfant';
|
||||
kid.setAttribute('aria-label','Ouvrir le chat enfant, écrit uniquement');
|
||||
head.insertBefore(kid,head.querySelector('.av-head-actions'));
|
||||
const toolsGrid=root.querySelector('#avMoreDialog .av-more-grid');
|
||||
if(toolsGrid&&!toolsGrid.querySelector('a[href="/enfant"]')){
|
||||
const a=document.createElement('a');a.href='/enfant';a.textContent='⭐ Espace enfant';toolsGrid.append(a);
|
||||
}
|
||||
|
||||
// New expressive centre; old controls stay in DOM so audio event listeners work.
|
||||
const display=document.createElement('div');display.className='dona-art-center';
|
||||
display.innerHTML='<div class="dona-organic-wrap"><canvas id="donaOrganicCanvas" aria-label="Orbe animé de Dona" role="img"></canvas><span class="dona-orb-reflection" aria-hidden="true"></span></div><strong class="dona-art-name" id="donaArtName"></strong><span class="dona-art-subtitle" id="donaArtSubtitle">Prêt à vous aider</span>';
|
||||
hero.prepend(display);
|
||||
const oldVoice=root.querySelector('.av-voice-center');
|
||||
if(oldVoice){display.append(oldVoice);}
|
||||
|
||||
// Move shortcuts into a collapsed tray, instead of removing their controls.
|
||||
const tools=document.createElement('details');tools.className='dona-tools-tray';
|
||||
const summary=document.createElement('summary');summary.textContent='☷ Raccourcis';
|
||||
shortcuts.parentNode.insertBefore(tools,shortcuts);tools.append(summary,shortcuts);
|
||||
|
||||
// Minimal permanent one-row text field: no expandable tray, send at right.
|
||||
const compose=document.createElement('div');compose.className='dona-compose dona-compose-compact';
|
||||
compose.innerHTML=`<form class="dona-compose-body" id="donaComposeBody">
|
||||
<label class="dona-sr-only" for="donaComposeInput">Écrire à Dona</label>
|
||||
<div class="dona-compose-row"><textarea id="donaComposeInput" maxlength="1600" rows="1" placeholder="Écrire à Dona…" aria-label="Écrire à Dona" required></textarea><button id="donaComposeSend" type="submit" aria-label="Envoyer la demande" title="Envoyer">➤</button></div>
|
||||
<span id="donaComposeStatus" role="status" aria-live="polite"></span>
|
||||
</form>`;
|
||||
root.append(compose);
|
||||
const body=$('donaComposeBody'),input=$('donaComposeInput'),send=$('donaComposeSend'),status=$('donaComposeStatus');
|
||||
input.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();body.requestSubmit();}});
|
||||
body.addEventListener('submit',async e=>{
|
||||
e.preventDefault();let message=input.value.trim();if(!message)return;
|
||||
send.disabled=true;status.textContent='Envoi à Hermes…';
|
||||
try{
|
||||
const r=await fetch('/api/dona/texte',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json','X-Alfred-Request':'1'},body:JSON.stringify({message})});
|
||||
let d={};try{d=await r.json();}catch(_){}
|
||||
if(!r.ok)throw Error(d.error||'Erreur '+r.status);
|
||||
input.value='';status.textContent='Hermes prépare sa réponse…';
|
||||
}catch(err){status.textContent=err.message;}
|
||||
finally{send.disabled=false;}
|
||||
});
|
||||
const modeText={idle:'Prêt à vous aider',waiting:'Prêt à vous aider',listening:'Je vous écoute…',thinking:'Je réfléchis…',speaking:'Je vous réponds…',error:'Un problème est survenu'};
|
||||
function setMode(mode){root.dataset.familyMode=mode;const t=$('donaArtSubtitle');if(t)t.textContent=modeText[mode]||'Prêt à vous aider';if(mode==='idle'&&status.textContent==='Hermes prépare sa réponse…')status.textContent='Réponse reçue';}
|
||||
function setName(value){const name=String(value||$('avName')?.textContent||'Dona').trim();$('donaArtName').textContent=name;}
|
||||
setName();setMode(root.dataset.mode||'idle');
|
||||
const socket=window.alfredSocket;
|
||||
socket?.on('alfred_state',s=>{setMode(String(s.mode||'idle').toLowerCase());setName(s.assistant_name||s.title||$('avName')?.textContent);});
|
||||
socket?.on('alfred_render',()=>{if(status.textContent==='Hermes prépare sa réponse…')status.textContent='Réponse reçue';});
|
||||
fetch('/api/state',{cache:'no-store'}).then(r=>r.ok?r.json():null).then(s=>{if(s){setMode(s.mode||'idle');setName(s.assistant_name||s.title);}}).catch(()=>{});
|
||||
|
||||
// Animated multi-layer liquid orb using canvas, independent of server state.
|
||||
const canvas=$('donaOrganicCanvas'),ctx=canvas.getContext('2d');
|
||||
if(!ctx)return;
|
||||
let w=320,h=320,phase=0,raf=0,last=0;
|
||||
const reduce=matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||
function resize(){const rect=canvas.getBoundingClientRect();const dpr=Math.min(devicePixelRatio||1,2);w=Math.max(150,rect.width);h=Math.max(150,rect.height);canvas.width=Math.floor(w*dpr);canvas.height=Math.floor(h*dpr);ctx.setTransform(dpr,0,0,dpr,0,0);}
|
||||
window.addEventListener('resize',resize,{passive:true});resize();
|
||||
function path(radius,layer){const n=130;ctx.beginPath();for(let k=0;k<=n;k++){
|
||||
const a=k/n*Math.PI*2;const modulation=1+0.067*Math.sin(a*3+phase*.72+layer*.64)+0.044*Math.cos(a*5-phase*.39+layer)+0.021*Math.sin(a*8+phase*.55);
|
||||
const r=radius*modulation;const x=Math.cos(a)*r,y=Math.sin(a)*r;
|
||||
if(k===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);
|
||||
}ctx.closePath();}
|
||||
function frame(now){raf=requestAnimationFrame(frame);if(document.hidden||now-last<30)return;last=now;
|
||||
const mode=root.dataset.familyMode||'idle';const speed=mode==='listening'?1.7:mode==='thinking'?1.38:mode==='speaking'?1.5:.75;
|
||||
if(!reduce)phase+=.024*speed;
|
||||
ctx.clearRect(0,0,w,h);ctx.save();ctx.translate(w/2,h/2);const r=Math.min(w,h)*.33;
|
||||
const bloom=ctx.createRadialGradient(-r*.2,-r*.15,r*.1,0,0,r*1.45);
|
||||
bloom.addColorStop(0,'rgba(238,117,160,.11)');bloom.addColorStop(.7,'rgba(139,112,246,.14)');bloom.addColorStop(1,'rgba(37,180,255,0)');ctx.fillStyle=bloom;ctx.beginPath();ctx.arc(0,0,r*1.48,0,Math.PI*2);ctx.fill();
|
||||
for(let layer=3;layer>=0;layer--){const waveR=r*(1+layer*.025);path(waveR,layer);
|
||||
const g=ctx.createLinearGradient(-r*.9,r*.8,r*.9,-r*.85);
|
||||
if(layer%2===0){g.addColorStop(0,'rgba(47,196,244,.76)');g.addColorStop(.36,'rgba(68,94,198,.88)');g.addColorStop(.62,'rgba(244,138,173,.74)');g.addColorStop(1,'rgba(255,176,71,.9)');}
|
||||
else {g.addColorStop(0,'rgba(59,164,255,.65)');g.addColorStop(.4,'rgba(196,119,245,.48)');g.addColorStop(.7,'rgba(241,125,146,.52)');g.addColorStop(1,'rgba(255,185,95,.76)');}
|
||||
ctx.globalAlpha=layer===0?.76:.25;ctx.fillStyle=g;ctx.fill();
|
||||
ctx.globalAlpha=layer===0?.92:.31;ctx.strokeStyle=g;ctx.lineWidth=layer===0?2.5:1.8;ctx.shadowBlur=layer===0?23:13;ctx.shadowColor=layer%2?'#bd80ec':'#ffc685';ctx.stroke();ctx.shadowBlur=0;
|
||||
}
|
||||
const ribbon=ctx.createLinearGradient(-r,r*.4,r,-r*.3);ribbon.addColorStop(0,'rgba(67,219,255,.0)');ribbon.addColorStop(.48,'rgba(255,231,197,.56)');ribbon.addColorStop(1,'rgba(255,181,103,.01)');
|
||||
ctx.beginPath();for(let i=0;i<=100;i++){const x=-r+i/100*r*2;const y=Math.sin(i/100*Math.PI*2+phase*.35)*r*.25+Math.cos(i/100*Math.PI*3-phase*.45)*r*.11;if(i===0)ctx.moveTo(x,y);else ctx.lineTo(x,y);}ctx.lineWidth=r*.26;ctx.strokeStyle=ribbon;ctx.globalAlpha=.38;ctx.stroke();ctx.restore();
|
||||
}
|
||||
raf=requestAnimationFrame(frame);window.addEventListener('pagehide',()=>cancelAnimationFrame(raf),{once:true});
|
||||
}
|
||||
init();
|
||||
})();
|
||||
@@ -0,0 +1,258 @@
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="theme-color" content="#202326">
|
||||
<title>{{ name }}</title>
|
||||
<link rel="stylesheet" href="/static/css/alfred.css">
|
||||
<link rel="stylesheet" href="/static/css/alfred-controls.css">
|
||||
<link rel="stylesheet" href="/static/css/alfred-views.css">
|
||||
<script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/css/alfred-camera-popup.css">
|
||||
<link rel="stylesheet" href="/static/css/alfred-features.css?v=45">
|
||||
<link rel="stylesheet" href="/static/css/alfred-timers-context.css">
|
||||
<!-- ALFRED_MAIN_VISUAL_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-main-v1.css?v=1">
|
||||
<!-- ALFRED_LISTES_GLASS_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-listes-glass.css?v=1">
|
||||
<!-- ALFRED_GLASS_V2 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-main-v2.css?v=2">
|
||||
<!-- ALFRED_STT_INLINE_GLASS_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-stt-inline-v1.css?v=1">
|
||||
<!-- ALFRED_REPONSE_CLOSE_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-reponse-close-v1.css?v=1">
|
||||
<!-- ALFRED_HERMES_NEW_SESSION_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/alfred-hermes-new-session.css?v=1">
|
||||
<!-- DONA_HOLO_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-holographic-v1.css?v=md1">
|
||||
<!-- DONA_FAMILY_UI_V1 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-accueil-ecrit-v1.css?v=1">
|
||||
<link rel="stylesheet" href="/static/css/dona-v6.css?v=6">
|
||||
<!-- DONA_V7_RELEASE -->
|
||||
<link rel="stylesheet" href="/static/css/dona-v7.css?v=7">
|
||||
<!-- DONA_V8_DYNAMIC -->
|
||||
<link rel="stylesheet" href="/static/css/dona-dynamic-v8.css?v=8">
|
||||
<!-- DONA_V8_1_ISOLATION -->
|
||||
<link rel="stylesheet" href="/static/css/dona-header-v8-1.css?v=81">
|
||||
<!-- DONA_YOUTUBE_V82 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-youtube-v8-2.css?v=82">
|
||||
<!-- DONA_NAV_V83 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-navigation-v8-3.css?v=83">
|
||||
<!-- DONA_SUITE_V9 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-suite-v9.css?v=90">
|
||||
<!-- DONA_FILES_V91 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-files-v9-1.css?v=91">
|
||||
<!-- DONA_UI_V92 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-ui-v9-2.css?v=92">
|
||||
<!-- DONA_UI_V921_CLOSE -->
|
||||
<link rel="stylesheet" href="/static/css/dona-ui-v9-2-1-close.css?v=921">
|
||||
<!-- DONA_LEARNING_V93 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-learning-v9-3.css?v=93">
|
||||
<!-- DONA_VISION_V95 -->
|
||||
<link rel="stylesheet" href="/static/css/dona-vision-v9-5.css?v=95">
|
||||
</head>
|
||||
<body>
|
||||
<main class="alfred-shell">
|
||||
<header class="topbar">
|
||||
<!-- Le statut remplace l'ancien titre « Alfred ». -->
|
||||
<div class="status" role="status" aria-live="polite">
|
||||
<span id="statusDot" class="status-dot" data-mode="idle" aria-hidden="true"></span>
|
||||
<span id="mode" data-mode="idle">En attente</span>
|
||||
</div>
|
||||
|
||||
<div class="header-actions" aria-label="Navigation et commandes audio">
|
||||
<!-- ALFRED_ADMIN_NAV_V1 : pages d’administration séparées -->
|
||||
<a class="icon-button" style="text-decoration:none" href="/config" title="Configuration complète" aria-label="Configuration complète">⚙</a>
|
||||
<a class="icon-button" style="text-decoration:none" href="/surveillance" title="Surveillance des services" aria-label="Surveillance des services">▤</a>
|
||||
<button id="viewHome" class="icon-button" type="button" title="Accueil" aria-label="Accueil">⌂</button>
|
||||
<button id="viewCalendar" class="icon-button" type="button" title="Agenda" aria-label="Agenda">▦</button>
|
||||
<button id="viewWeather" class="icon-button" type="button" title="Météo Home Assistant" aria-label="Météo Home Assistant">☀</button>
|
||||
<button id="viewTemperatures" class="icon-button" type="button" title="Températures" aria-label="Températures">°</button>
|
||||
<button id="viewCameras" class="icon-button" type="button" title="Caméras Home Assistant" aria-label="Caméras Home Assistant">▣</button>
|
||||
|
||||
<!-- ALFRED_CAMERA_POPUP_V1 -->
|
||||
<button id="cameraPopupButton" class="icon-button" type="button"
|
||||
title="Afficher la caméra" aria-label="Afficher la caméra"
|
||||
aria-haspopup="dialog" aria-controls="cameraPopup" aria-expanded="false">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"
|
||||
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="2.5" y="5" width="14" height="14" rx="3"/>
|
||||
<path d="m16.5 10 5-3v10l-5-3"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="audioSettingsButton" class="icon-button" type="button"
|
||||
aria-label="Réglages audio et caméra" aria-haspopup="dialog" aria-expanded="false"
|
||||
aria-controls="audioSettingsPanel" title="Réglages audio et caméra">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M4 7h16M4 17h16"/><circle cx="9" cy="7" r="2.5" fill="#272b30"/><circle cx="16" cy="17" r="2.5" fill="#272b30"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="micMute" class="icon-button" type="button"
|
||||
aria-label="Couper le microphone" aria-pressed="false" title="Couper le microphone">
|
||||
<svg class="mic-on-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="9" y="2.5" width="6" height="12" rx="3"/>
|
||||
<path d="M5.5 10.5v1.5a6.5 6.5 0 0 0 13 0v-1.5M12 18.5v3M8 21.5h8"/>
|
||||
</svg>
|
||||
<svg class="mic-off-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="M9.2 5.3V5.5a3 3 0 0 1 5.5-1.4M15 10.6v.9c0 .5-.1 1-.3 1.5M9 9.5v2A3 3 0 0 0 13.6 14"/>
|
||||
<path d="M5.5 10.5V12a6.5 6.5 0 0 0 11.5 4.1M12 18.5v3M8 21.5h8M3 3l18 18"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button id="speakerMuteQuick" class="icon-button" type="button"
|
||||
aria-label="Couper le son" aria-pressed="false" title="Couper le son">
|
||||
<svg class="speaker-on-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="m11 5-5 4H3v6h3l5 4V5Z"/><path d="M15 9a4 4 0 0 1 0 6M18 6a8 8 0 0 1 0 12"/>
|
||||
</svg>
|
||||
<svg class="speaker-off-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<path d="m11 5-5 4H3v6h3l5 4V5Z"/><path d="m16 9 5 6m0-6-5 6"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Pop-up : commandes connectées à /api/audio/settings -->
|
||||
<div id="audioSettingsPanel" class="audio-popover" role="dialog"
|
||||
aria-label="Réglages audio et caméra" aria-modal="false" hidden>
|
||||
<div class="popover-heading">
|
||||
<strong>Réglages</strong>
|
||||
<button id="closeAudioSettings" class="icon-button mini" type="button" aria-label="Fermer les réglages">×</button>
|
||||
</div>
|
||||
<div class="settings-tabs" role="tablist" aria-label="Catégories de réglages">
|
||||
<button id="audioTab" class="settings-tab" type="button" role="tab" aria-selected="true" aria-controls="audioSettingsSection">Audio</button>
|
||||
<button id="cameraTab" class="settings-tab" type="button" role="tab" aria-selected="false" aria-controls="cameraSettingsSection">Caméra</button>
|
||||
</div>
|
||||
<div id="audioSettingsSection" role="tabpanel" aria-labelledby="audioTab">
|
||||
<div class="setting-row">
|
||||
<label for="volumeSlider">Volume du haut-parleur</label>
|
||||
<output id="volumeValue" for="volumeSlider">70 %</output>
|
||||
</div>
|
||||
<input id="volumeSlider" type="range" min="0" max="100" value="70" step="1">
|
||||
<div class="setting-row">
|
||||
<label for="micGainSlider">Sensibilité du microphone</label>
|
||||
<output id="micGainValue" for="micGainSlider">—</output>
|
||||
</div>
|
||||
<input id="micGainSlider" type="range" min="0" max="100" value="50" step="1">
|
||||
<p id="audioSettingsMessage" class="settings-message" role="status" aria-live="polite"></p>
|
||||
</div>
|
||||
<div id="cameraSettingsSection" role="tabpanel" aria-labelledby="cameraTab" hidden>
|
||||
<p class="camera-settings-note">Webcam locale · réglages matériels V4L2.<br>Tu peux voir le résultat via le bouton Caméra en haut de l’écran.</p>
|
||||
<div id="cameraSettingsItems" class="camera-settings-items" aria-live="off"></div>
|
||||
<div class="camera-settings-actions">
|
||||
<button id="cameraSettingsReload" class="camera-settings-action" type="button">Actualiser</button>
|
||||
<button id="cameraSettingsReset" class="camera-settings-action reset" type="button">Valeurs d’usine</button>
|
||||
</div>
|
||||
<p id="cameraSettingsMessage" class="settings-message" role="status" aria-live="polite"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cameraPopup" class="camera-popup-overlay" role="dialog"
|
||||
aria-label="Prévisualisation caméra" aria-modal="true" hidden>
|
||||
<div class="camera-popup-frame">
|
||||
<div class="camera-popup-titlebar">
|
||||
<strong class="camera-popup-title">Caméra en direct</strong>
|
||||
<button id="cameraPopupClose" class="camera-popup-close" type="button"
|
||||
aria-label="Fermer la caméra" title="Fermer">×</button>
|
||||
</div>
|
||||
<aside class="camera-panel camera-popup-body" aria-label="Prévisualisation caméra">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title"><span class="panel-indicator"></span> CAMÉRA</span>
|
||||
<button id="cameraToggle" class="panel-action" type="button" aria-pressed="false">Activer</button>
|
||||
</div>
|
||||
<div class="camera-viewport">
|
||||
<img id="cameraFeed" alt="Vidéo en direct de la caméra d'Alfred" hidden>
|
||||
<div id="cameraPlaceholder" class="camera-placeholder">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4" aria-hidden="true">
|
||||
<rect x="2.5" y="5" width="14" height="14" rx="3"/><path d="m16.5 10 5-3v10l-5-3"/>
|
||||
</svg>
|
||||
<span id="cameraMessage">Caméra désactivée</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<p class="camera-popup-footnote">Le flux est interrompu à la fermeture pour laisser la webcam disponible aux autres fonctions d’Alfred.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section id="stage" aria-label="Réponse d'Alfred">
|
||||
<div id="content"><h1>Bonjour.</h1><p>À votre écoute.</p></div>
|
||||
</section>
|
||||
|
||||
<section id="wave" aria-hidden="true"><canvas id="waveCanvas"></canvas></section>
|
||||
|
||||
|
||||
|
||||
<!-- Affiche les résultats partiels ET définitifs de la reconnaissance Vosk. -->
|
||||
<aside class="transcript-panel" aria-label="Transcription vocale">
|
||||
<div class="panel-heading">
|
||||
<span class="panel-title"><span class="panel-indicator stt-indicator"></span> RECONNAISSANCE VOCALE · VOSK</span>
|
||||
<span id="transcriptState" class="transcript-state">En attente</span>
|
||||
</div>
|
||||
<p id="transcriptText" class="transcript-text" aria-live="polite">La dernière phrase reconnue s'affichera ici.</p>
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
<script src="/static/js/alfred.js"></script>
|
||||
<script src="/static/js/alfred-views.js"></script>
|
||||
<script src="/static/js/alfred-controls.js"></script>
|
||||
<script src="/static/js/alfred-camera.js"></script>
|
||||
<script src="/static/js/alfred-camera-settings.js"></script>
|
||||
<script src="/static/js/alfred-camera-popup.js"></script>
|
||||
<script defer src="/static/js/alfred-features.js?v=45"></script>
|
||||
|
||||
<!-- ALFRED_TIMERS_CONTEXT_V1 : chronos/minuteurs dans le coin inférieur gauche -->
|
||||
<aside id="alfredTimersDock" role="region" aria-label="Minuteurs et chronomètres" hidden>
|
||||
<div class="alfred-time-head">⏱ MINUTEURS ET CHRONOMÈTRES</div>
|
||||
<div id="alfredTimersList"></div>
|
||||
</aside>
|
||||
<div id="alfredTimerToast" role="alert" aria-live="assertive" hidden></div>
|
||||
|
||||
<div id="alfredContextPopup" role="dialog" aria-label="Illustration de la réponse"
|
||||
aria-modal="true" hidden>
|
||||
<article class="alfred-context-card">
|
||||
<button id="alfredContextClose" class="alfred-context-close" type="button"
|
||||
aria-label="Fermer l'illustration" title="Fermer">×</button>
|
||||
<img id="alfredContextImage" alt="Illustration contextuelle de la réponse" />
|
||||
<div class="alfred-context-details">
|
||||
<h2 id="alfredContextTitle"></h2>
|
||||
<p id="alfredContextText"></p>
|
||||
<a id="alfredContextSource" href="#" target="_blank" rel="noopener noreferrer">Source Wikipédia</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<script src="/static/js/alfred-timers-context.js"></script>
|
||||
<!-- ALFRED_HIDE_HERMES_ACTIVITY_V2 -->
|
||||
<script src="/static/js/alfred-hide-hermes-activity.js?v=2" defer></script>
|
||||
<script src="/static/js/alfred-main-v1.js?v=1"></script>
|
||||
<!-- ALFRED_ADMIN_GLASS_MAISON_V1 -->
|
||||
<script src="/static/js/alfred-maison-card.js?v=1"></script>
|
||||
<script src="/static/js/alfred-listes-glass.js?v=1"></script>
|
||||
<script src="/static/js/alfred-main-v2.js?v=2"></script>
|
||||
<!-- ALFRED_IDENTITY_GLASS_V1 -->
|
||||
<script src="/static/js/alfred-name-dynamic.js?v=1" defer></script>
|
||||
<script src="/static/js/alfred-stt-inline-v1.js?v=1"></script>
|
||||
<script src="/static/js/alfred-reponse-close-v1.js?v=1"></script>
|
||||
<!-- ALFRED_DEVELOPER_V1 -->
|
||||
<script src="/static/js/alfred-maintenance-link.js?v=1" defer></script>
|
||||
<script src="/static/js/alfred-hermes-new-session.js?v=1"></script>
|
||||
<script defer src="/static/js/dona-markdown-renderer.js?v=md1"></script>
|
||||
<script defer src="/static/js/dona-holographic-v1.js?v=md1"></script>
|
||||
<!-- DONA_REPLY_AUTO_CLOSE_V1 -->
|
||||
<script defer src="/static/js/dona-reply-auto-close-v7.js?v=7"></script>
|
||||
<script src="/static/js/dona-accueil-ecrit-v1.js?v=6"></script>
|
||||
<script defer src="/static/js/dona-spotify.js?v=6"></script>
|
||||
<!-- dona-ui-fix start -->
|
||||
<script defer src="{{ url_for('static', filename='js/dona-ui-fix.js') }}?v=6"></script>
|
||||
<!-- dona-ui-fix end -->
|
||||
<script defer src="/static/js/dona-face-v7.js?v=7"></script>
|
||||
<script defer src="/static/js/dona-dynamic-v8.js?v=8"></script>
|
||||
<script defer src="/static/js/dona-header-v8-1.js?v=81"></script>
|
||||
<script defer src="/static/js/dona-youtube-v8-2.js?v=82"></script>
|
||||
<script defer src="/static/js/dona-navigation-v8-3.js?v=83"></script>
|
||||
<script defer src="/static/js/dona-suite-v9.js?v=90"></script>
|
||||
<script defer src="/static/js/dona-files-v9-1.js?v=91"></script>
|
||||
<script defer src="/static/js/dona-ui-v9-2.js?v=92"></script>
|
||||
<script defer src="/static/js/dona-ui-v9-2-1-close.js?v=921"></script>
|
||||
<script defer src="/static/js/dona-learning-v9-3.js?v=93"></script>
|
||||
<script defer src="/static/js/dona-vision-v9-5.js?v=95"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user