From d15d40eb7c3563101fb4f8849cf4ada715b83d25 Mon Sep 17 00:00:00 2001 From: nathaninline Date: Wed, 5 Aug 2026 21:29:33 +0200 Subject: [PATCH] Modale preset : ouverture en haut et bloc moteur stabilise, pastilles redondantes retirees --- internal/ajean/ui/index.html | 29 +++++++++++++++---------- internal/ajean/ui/src/js/06-settings.js | 7 +++--- internal/ajean/ui/src/js/07-models.js | 15 +++++++++---- internal/ajean/ui/src/js/12-mcp.js | 7 +++--- 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/internal/ajean/ui/index.html b/internal/ajean/ui/index.html index aaa9e44..0b788f2 100644 --- a/internal/ajean/ui/index.html +++ b/internal/ajean/ui/index.html @@ -2215,10 +2215,11 @@ function renderInternet(s){ if(cf) cf.style.display = (webEngine==='crawl4ai') ? '' : 'none'; if(document.activeElement !== document.getElementById('crawl-url')) document.getElementById('crawl-url').value = s.url || ''; - // Actif mais serveur injoignable : la pastille le dit (sinon l'UI ment — les - // outils web ne sont pas proposés au modèle dans ce cas). + // La pastille ne redit PAS ce que l'interrupteur montre déjà (actif/inactif) : + // elle ne sert qu'à signaler l'anomalie — actif mais serveur injoignable, cas + // où les outils web ne sont pas proposés au modèle. if(internetOn && !s.reachable) setBadge('internet-badge','warn','injoignable'); - else setBadge('internet-badge', internetOn, internetOn?'actif':'inactif'); + else setBadge('internet-badge', null); // Clé du serveur Crawl4AI : le champ reste vide (la clé n'est jamais renvoyée), // on indique seulement si une clé est enregistrée. const ks=document.getElementById('crawl-key-state'), kc=document.getElementById('crawl-key-clear'); @@ -2397,9 +2398,9 @@ const KINDS = { // si le bouton était mort. On affiche la coquille tout de suite, puis on la // remplit. `openSeq` protège du cas « deux ouvertures coup sur coup » : une // réponse en retard ne doit jamais écraser la modale ouverte après elle. -// Pendant ce remplissage la modale est en `.loading` : son corps est estompé et -// inerte, puis révélé d'un coup une fois TOUT en place — sinon on voyait les -// champs se peupler un par un, ce qui donnait une impression de bricolage. +// Pendant ce remplissage la modale est en `.loading` : un rond tourne à la place +// du formulaire, qui n'est révélé qu'une fois TOUT en place (contenu, réglages, +// backends, GPU) — sinon on voyait les champs se peupler un par un. let openSeq = 0; async function openItem(kind, key){ const K = KINDS[kind]; @@ -2438,6 +2439,10 @@ async function openItem(kind, key){ rawBody.style.display = ''; } document.getElementById('modal').classList.add('loading'); + // Le corps de la modale est un conteneur défilant RÉUTILISÉ d'une ouverture à + // l'autre : sans cette remise à zéro, ouvrir un preset après en avoir fait + // défiler un autre rouvrait la fiche au milieu. + const peBody=document.querySelector('#modal .pe-body'); if(peBody) peBody.scrollTop=0; showModal('modal'); // --- Remplissage, une fois la modale à l'écran ----------------------------- const r = await jfetch(K.getUrl + '?' + K.param + '=' + encodeURIComponent(key||'')); @@ -2640,7 +2645,10 @@ async function populateBackend(){ if(radio) radio.checked = true; toggleBackendCustom(mode); if(mode === 'custom') document.getElementById('m-backend-path').value = cur; - loadGpuDevices(); + // Attendu (et non lancé dans le vide) : la liste des GPU change la hauteur du + // bloc « moteur ». Sans ce await, elle arrivait APRÈS la levée du voile de + // chargement et on voyait le bloc bouger tout seul. + await loadGpuDevices(); } function toggleBackendCustom(mode){ document.getElementById('m-backend-custom').style.display = (mode==='custom') ? 'block' : 'none'; @@ -4051,10 +4059,9 @@ async function loadMCP(){ renderMCP(await jget('/api/mcp')); } function renderMCP(r){ mcpServers = (r && r.servers) || []; - const enabled = mcpServers.filter(s=>s.enabled).length; - if(enabled) setBadge('mcp-badge', true, enabled+(enabled>1?' actifs':' actif')); - else if(mcpServers.length) setBadge('mcp-badge', false, 'aucun actif'); - else setBadge('mcp-badge', null); + // Pas de pastille de comptage : chaque serveur affiche déjà son propre état + // dans la liste juste en dessous. + setBadge('mcp-badge', null); const list = document.getElementById('mcp-list'); list.textContent = ''; if(!mcpServers.length){ diff --git a/internal/ajean/ui/src/js/06-settings.js b/internal/ajean/ui/src/js/06-settings.js index 0275dd1..1f1ebd7 100644 --- a/internal/ajean/ui/src/js/06-settings.js +++ b/internal/ajean/ui/src/js/06-settings.js @@ -147,10 +147,11 @@ function renderInternet(s){ if(cf) cf.style.display = (webEngine==='crawl4ai') ? '' : 'none'; if(document.activeElement !== document.getElementById('crawl-url')) document.getElementById('crawl-url').value = s.url || ''; - // Actif mais serveur injoignable : la pastille le dit (sinon l'UI ment — les - // outils web ne sont pas proposés au modèle dans ce cas). + // La pastille ne redit PAS ce que l'interrupteur montre déjà (actif/inactif) : + // elle ne sert qu'à signaler l'anomalie — actif mais serveur injoignable, cas + // où les outils web ne sont pas proposés au modèle. if(internetOn && !s.reachable) setBadge('internet-badge','warn','injoignable'); - else setBadge('internet-badge', internetOn, internetOn?'actif':'inactif'); + else setBadge('internet-badge', null); // Clé du serveur Crawl4AI : le champ reste vide (la clé n'est jamais renvoyée), // on indique seulement si une clé est enregistrée. const ks=document.getElementById('crawl-key-state'), kc=document.getElementById('crawl-key-clear'); diff --git a/internal/ajean/ui/src/js/07-models.js b/internal/ajean/ui/src/js/07-models.js index b1399ec..f71cdf9 100644 --- a/internal/ajean/ui/src/js/07-models.js +++ b/internal/ajean/ui/src/js/07-models.js @@ -62,9 +62,9 @@ const KINDS = { // si le bouton était mort. On affiche la coquille tout de suite, puis on la // remplit. `openSeq` protège du cas « deux ouvertures coup sur coup » : une // réponse en retard ne doit jamais écraser la modale ouverte après elle. -// Pendant ce remplissage la modale est en `.loading` : son corps est estompé et -// inerte, puis révélé d'un coup une fois TOUT en place — sinon on voyait les -// champs se peupler un par un, ce qui donnait une impression de bricolage. +// Pendant ce remplissage la modale est en `.loading` : un rond tourne à la place +// du formulaire, qui n'est révélé qu'une fois TOUT en place (contenu, réglages, +// backends, GPU) — sinon on voyait les champs se peupler un par un. let openSeq = 0; async function openItem(kind, key){ const K = KINDS[kind]; @@ -103,6 +103,10 @@ async function openItem(kind, key){ rawBody.style.display = ''; } document.getElementById('modal').classList.add('loading'); + // Le corps de la modale est un conteneur défilant RÉUTILISÉ d'une ouverture à + // l'autre : sans cette remise à zéro, ouvrir un preset après en avoir fait + // défiler un autre rouvrait la fiche au milieu. + const peBody=document.querySelector('#modal .pe-body'); if(peBody) peBody.scrollTop=0; showModal('modal'); // --- Remplissage, une fois la modale à l'écran ----------------------------- const r = await jfetch(K.getUrl + '?' + K.param + '=' + encodeURIComponent(key||'')); @@ -305,7 +309,10 @@ async function populateBackend(){ if(radio) radio.checked = true; toggleBackendCustom(mode); if(mode === 'custom') document.getElementById('m-backend-path').value = cur; - loadGpuDevices(); + // Attendu (et non lancé dans le vide) : la liste des GPU change la hauteur du + // bloc « moteur ». Sans ce await, elle arrivait APRÈS la levée du voile de + // chargement et on voyait le bloc bouger tout seul. + await loadGpuDevices(); } function toggleBackendCustom(mode){ document.getElementById('m-backend-custom').style.display = (mode==='custom') ? 'block' : 'none'; diff --git a/internal/ajean/ui/src/js/12-mcp.js b/internal/ajean/ui/src/js/12-mcp.js index 4ae7eed..da5a746 100644 --- a/internal/ajean/ui/src/js/12-mcp.js +++ b/internal/ajean/ui/src/js/12-mcp.js @@ -9,10 +9,9 @@ async function loadMCP(){ renderMCP(await jget('/api/mcp')); } function renderMCP(r){ mcpServers = (r && r.servers) || []; - const enabled = mcpServers.filter(s=>s.enabled).length; - if(enabled) setBadge('mcp-badge', true, enabled+(enabled>1?' actifs':' actif')); - else if(mcpServers.length) setBadge('mcp-badge', false, 'aucun actif'); - else setBadge('mcp-badge', null); + // Pas de pastille de comptage : chaque serveur affiche déjà son propre état + // dans la liste juste en dessous. + setBadge('mcp-badge', null); const list = document.getElementById('mcp-list'); list.textContent = ''; if(!mcpServers.length){