diff --git a/internal/loki/ui/index.html b/internal/loki/ui/index.html index b68702e..3287954 100644 --- a/internal/loki/ui/index.html +++ b/internal/loki/ui/index.html @@ -27,7 +27,7 @@ barre s'affiche puis se referme sous les yeux à chaque chargement. Ouverts par défaut — une valeur absente vaut « visible ». */ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-side')==='0'?'0':'1'); -document.documentElement.setAttribute('data-perf',localStorage.getItem('loki-perf')==='0'?'0':'1');}catch(e){document.documentElement.setAttribute('data-theme','light');} +}catch(e){document.documentElement.setAttribute('data-theme','light');}
-
Performance - -
+
Performance
…
@@ -3000,7 +3054,7 @@ function updateCtxMeter(){ // Construit EN DOM et non en innerHTML : les noms de GPU viennent du pilote, // donc d'ailleurs. Même règle que pour les noms de fichiers et les titres de // discussions. -function perfRow(label, pct, sub, title){ +function perfRow(label, pct, title){ pct = Math.max(0, Math.min(100, Math.round(pct||0))); const row=document.createElement('div'); row.className='perf-row'; if(title) row.title=title; @@ -3016,7 +3070,6 @@ function perfRow(label, pct, sub, title){ fill.style.width=pct+'%'; track.appendChild(fill); row.append(l,track); - if(sub){ const s=document.createElement('div'); s.className='perf-sub'; s.textContent=sub; row.appendChild(s); } return row; } async function loadVram(){ @@ -3029,14 +3082,16 @@ async function loadVram(){ box.appendChild(d); return; } + // Deux mesures par carte, deux jauges — une seule barre ne peut pas porter la + // charge du processeur ET le remplissage de la mémoire vidéo, et pour du LLM + // c'est la VRAM qui décide si un modèle tient. Sur une machine à UNE carte on + // retombe exactement sur les trois jauges de la maquette (GPU, VRAM, RAM). + const many = gpus.length > 1; gpus.forEach((g,i)=>{ - // Le pourcentage affiché est la CHARGE du processeur graphique (ce que - // montre la maquette) ; la VRAM, elle, passe en détail sous la barre — deux - // mesures distinctes qu'une seule barre ne peut pas porter. const vram=(g.used/1024).toFixed(1)+' / '+(g.total/1024).toFixed(1)+' Gio'; - box.appendChild(perfRow( - 'GPU '+i+(gpus.length>1?'':'')+' — charge', g.util, - vram+' · '+g.temp+' °C', g.name)); + box.appendChild(perfRow('GPU '+i, g.util, g.name+' · '+vram+' · '+g.temp+' °C')); + box.appendChild(perfRow(many ? 'VRAM '+i : 'VRAM', g.used*100/g.total, + g.name+' · '+vram)); }); } async function loadRam(){ @@ -3048,7 +3103,7 @@ async function loadRam(){ const host=document.getElementById('ram'); host.textContent=''; host.appendChild(perfRow('Mémoire vive', m.used*100/m.total, - (m.used/1024).toFixed(1)+' / '+(m.total/1024).toFixed(1)+' Gio')); + (m.used/1024).toFixed(1)+' / '+(m.total/1024).toFixed(1)+' Gio utilisés')); } async function loadCfg(){ // /api/llamacpp en parallèle : il indique si le BIN de la config correspond au @@ -6782,18 +6837,6 @@ function toggleSideCollapse(){ if(typeof syncGutter === 'function') requestAnimationFrame(syncGutter); } -// Moniteur machine replié : le pied de barre latérale ne garde que son titre. -function togglePerf(){ - const on = document.documentElement.getAttribute('data-perf') !== '0'; - document.documentElement.setAttribute('data-perf', on ? '0' : '1'); - try{ localStorage.setItem('loki-perf', on ? '0' : '1'); }catch(_){} - const b = document.querySelector('#side-perf .perf-toggle'); - if(b){ - b.textContent = on ? '+' : '–'; - b.title = on ? 'déplier le moniteur' : 'replier le moniteur'; - } -} - // Bascule clair/sombre depuis l'en-tête. Elle passe par applyTheme + savePrefs // (01-theme.js) pour rester alignée avec l'interrupteur des réglages et avec le // serveur — deux chemins vers le même réglage, une seule source de vérité. @@ -6886,12 +6929,4 @@ function paintModelSwitch(){ // Une bascule en cours ne doit pas voir sa sélection sauter sous la souris. if(prev && [...sel.options].some(o => o.value === prev)) sel.value = prev; } - -// État initial des libellés (le CSS, lui, est déjà appliqué par le ). -document.addEventListener('DOMContentLoaded', () => { - if(document.documentElement.getAttribute('data-perf') === '0'){ - const b = document.querySelector('#side-perf .perf-toggle'); - if(b){ b.textContent = '+'; b.title = 'déplier le moniteur'; } - } -}); diff --git a/internal/loki/ui/src/index.tmpl.html b/internal/loki/ui/src/index.tmpl.html index d11413b..b9fdd82 100644 --- a/internal/loki/ui/src/index.tmpl.html +++ b/internal/loki/ui/src/index.tmpl.html @@ -27,7 +27,7 @@ barre s'affiche puis se referme sous les yeux à chaque chargement. Ouverts par défaut — une valeur absente vaut « visible ». */ document.documentElement.setAttribute('data-side',localStorage.getItem('loki-side')==='0'?'0':'1'); -document.documentElement.setAttribute('data-perf',localStorage.getItem('loki-perf')==='0'?'0':'1');}catch(e){document.documentElement.setAttribute('data-theme','light');} +}catch(e){document.documentElement.setAttribute('data-theme','light');} @@ -349,9 +349,7 @@ document.documentElement.setAttribute('data-perf',localStorage.getItem('loki-per fil. Le détail chiffré (Gio, température) vit sous chaque jauge, et le bloc entier se replie d'un clic. -->
-
Performance - -
+
Performance
…
diff --git a/internal/loki/ui/src/js/05-status.js b/internal/loki/ui/src/js/05-status.js index de49932..57e9b67 100644 --- a/internal/loki/ui/src/js/05-status.js +++ b/internal/loki/ui/src/js/05-status.js @@ -137,7 +137,7 @@ function updateCtxMeter(){ // Construit EN DOM et non en innerHTML : les noms de GPU viennent du pilote, // donc d'ailleurs. Même règle que pour les noms de fichiers et les titres de // discussions. -function perfRow(label, pct, sub, title){ +function perfRow(label, pct, title){ pct = Math.max(0, Math.min(100, Math.round(pct||0))); const row=document.createElement('div'); row.className='perf-row'; if(title) row.title=title; @@ -153,7 +153,6 @@ function perfRow(label, pct, sub, title){ fill.style.width=pct+'%'; track.appendChild(fill); row.append(l,track); - if(sub){ const s=document.createElement('div'); s.className='perf-sub'; s.textContent=sub; row.appendChild(s); } return row; } async function loadVram(){ @@ -166,14 +165,16 @@ async function loadVram(){ box.appendChild(d); return; } + // Deux mesures par carte, deux jauges — une seule barre ne peut pas porter la + // charge du processeur ET le remplissage de la mémoire vidéo, et pour du LLM + // c'est la VRAM qui décide si un modèle tient. Sur une machine à UNE carte on + // retombe exactement sur les trois jauges de la maquette (GPU, VRAM, RAM). + const many = gpus.length > 1; gpus.forEach((g,i)=>{ - // Le pourcentage affiché est la CHARGE du processeur graphique (ce que - // montre la maquette) ; la VRAM, elle, passe en détail sous la barre — deux - // mesures distinctes qu'une seule barre ne peut pas porter. const vram=(g.used/1024).toFixed(1)+' / '+(g.total/1024).toFixed(1)+' Gio'; - box.appendChild(perfRow( - 'GPU '+i+(gpus.length>1?'':'')+' — charge', g.util, - vram+' · '+g.temp+' °C', g.name)); + box.appendChild(perfRow('GPU '+i, g.util, g.name+' · '+vram+' · '+g.temp+' °C')); + box.appendChild(perfRow(many ? 'VRAM '+i : 'VRAM', g.used*100/g.total, + g.name+' · '+vram)); }); } async function loadRam(){ @@ -185,7 +186,7 @@ async function loadRam(){ const host=document.getElementById('ram'); host.textContent=''; host.appendChild(perfRow('Mémoire vive', m.used*100/m.total, - (m.used/1024).toFixed(1)+' / '+(m.total/1024).toFixed(1)+' Gio')); + (m.used/1024).toFixed(1)+' / '+(m.total/1024).toFixed(1)+' Gio utilisés')); } async function loadCfg(){ // /api/llamacpp en parallèle : il indique si le BIN de la config correspond au diff --git a/internal/loki/ui/src/js/18-shell.js b/internal/loki/ui/src/js/18-shell.js index f8fff11..0f02f87 100644 --- a/internal/loki/ui/src/js/18-shell.js +++ b/internal/loki/ui/src/js/18-shell.js @@ -21,18 +21,6 @@ function toggleSideCollapse(){ if(typeof syncGutter === 'function') requestAnimationFrame(syncGutter); } -// Moniteur machine replié : le pied de barre latérale ne garde que son titre. -function togglePerf(){ - const on = document.documentElement.getAttribute('data-perf') !== '0'; - document.documentElement.setAttribute('data-perf', on ? '0' : '1'); - try{ localStorage.setItem('loki-perf', on ? '0' : '1'); }catch(_){} - const b = document.querySelector('#side-perf .perf-toggle'); - if(b){ - b.textContent = on ? '+' : '–'; - b.title = on ? 'déplier le moniteur' : 'replier le moniteur'; - } -} - // Bascule clair/sombre depuis l'en-tête. Elle passe par applyTheme + savePrefs // (01-theme.js) pour rester alignée avec l'interrupteur des réglages et avec le // serveur — deux chemins vers le même réglage, une seule source de vérité. @@ -125,11 +113,3 @@ function paintModelSwitch(){ // Une bascule en cours ne doit pas voir sa sélection sauter sous la souris. if(prev && [...sel.options].some(o => o.value === prev)) sel.value = prev; } - -// État initial des libellés (le CSS, lui, est déjà appliqué par le ). -document.addEventListener('DOMContentLoaded', () => { - if(document.documentElement.getAttribute('data-perf') === '0'){ - const b = document.querySelector('#side-perf .perf-toggle'); - if(b){ b.textContent = '+'; b.title = 'déplier le moniteur'; } - } -}); diff --git a/internal/loki/ui/src/styles.css b/internal/loki/ui/src/styles.css index 06497e9..e61e78d 100644 --- a/internal/loki/ui/src/styles.css +++ b/internal/loki/ui/src/styles.css @@ -16,14 +16,22 @@ data-theme="dark" sur . Toutes les couleurs de l'UI passent par ces variables ; rien d'autre ne code une couleur en dur. - ⚠️ Les valeurs sont RELEVÉES sur la maquette (projet Stitch « Loki AI Chat - Modern Redesign v1 », écrans clair et sombre), pas approchées à l'œil. Elles - posent une hiérarchie de surfaces que l'intuition inverse volontiers : + ⚠️ Les valeurs NOMMÉES viennent des deux planches officielles du projet Stitch + « Loki AI Chat Modern Redesign v1 » — « Official Palette for Loki AI v1 » et + « Official Dark Mode Palette for Loki AI » — pas d'une approximation à l'œil : - barre latérale la plus SOMBRE des trois #E4E8E9 / #0F1829 - fil la surface de référence #EFF3F4 / #1E2834 - carte s'ÉCARTE du fil #E5E9EA / #253141 - saisie la plus claire #EFF3F6 / #1F2734 + clair Main BG #F5F6F8 · Sidebar BG #ECECEC · Primary Accent #95A899 + Typography #333333 + sombre Deep Navy #0F172A · Slate Surface #1E293B · Sage Accent #84A98C + + Le reste (carte de réponse, carte de saisie, piste de jauge, bulle sombre) est + relevé sur les écrans de la maquette, qui posent une hiérarchie de surfaces + que l'intuition inverse volontiers : + + barre latérale la plus SOMBRE des trois #ECECEC / #0F172A + fil la surface de référence #F5F6F8 / #1E293B + carte s'ÉCARTE du fil #EAEBEE / #2A3646 + saisie la plus claire #FBFBFD / #1B2436 En clair la carte de réponse est donc plus sombre que le fil (et non blanche), en sombre plus claire : dans les deux cas elle se détache en s'éloignant du @@ -46,28 +54,28 @@ :root{color-scheme:light; --font:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif; --mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; - --bg:#eff3f4;--panel:#eff3f6;--bubble:#e5e9ea;--border:rgba(30,41,59,.10); - --text:#1e293b;--dim:#64748b; - --accent:#4a7059;--accent-bg:rgba(126,165,138,.18);--on-accent:#ffffff;--on-err:#ffffff; - --accent-fill:#7ea58a;--accent-soft:#7ea58a; - --user-bg:#7ea58a;--on-user:#ffffff; - --side:#e4e8e9;--row-bg:#e9edee;--row-bg-on:#d8dcdf;--track:#d5d9dc; - --ok:#4a7059;--warn:#a86a2a;--err:#c0503f;--mag:#a86a2a; - --code-bg:#dbe0e2;--danger-bg:#f5e3e0;--warn-bg:#f2ece1; - --add:#4a7059;--add-bg:rgba(126,165,138,.18);--del:#c0503f;--del-bg:rgba(192,80,63,.12); + --bg:#f5f6f8;--panel:#fbfbfd;--bubble:#eaebee;--border:rgba(51,51,51,.11); + --text:#333333;--dim:#6b7280; + --accent:#587062;--accent-bg:rgba(149,168,153,.20);--on-accent:#ffffff;--on-err:#ffffff; + --accent-fill:#95a899;--accent-soft:#95a899; + --user-bg:#95a899;--on-user:#ffffff; + --side:#ececec;--row-bg:#f0f0f1;--row-bg-on:#e0e0e2;--track:#dadbdf; + --ok:#587062;--warn:#a86a2a;--err:#c0503f;--mag:#a86a2a; + --code-bg:#e2e3e7;--danger-bg:#f5e3e0;--warn-bg:#f2ece1; + --add:#587062;--add-bg:rgba(149,168,153,.20);--del:#c0503f;--del-bg:rgba(192,80,63,.12); --shadow:0 1px 2px rgba(15,23,42,.05);--shadow-lift:0 4px 16px rgba(15,23,42,.08); --shadow-card:0 2px 10px rgba(15,23,42,.06),0 1px 3px rgba(15,23,42,.04)} /* « Deep Dark » : fond #0F172A, cartes #1E293B. */ [data-theme="dark"]{color-scheme:dark; - --bg:#1e2834;--panel:#1f2734;--bubble:#253141;--border:rgba(148,163,184,.16); + --bg:#1e293b;--panel:#1b2436;--bubble:#2a3646;--border:rgba(148,163,184,.16); --text:#e2e8f0;--dim:#94a3b8; - --accent:#9dbfa8;--accent-bg:rgba(119,150,132,.22);--on-accent:#0f1829;--on-err:#0f1829; - --accent-fill:#779684;--accent-soft:#779684; - --user-bg:#334257;--on-user:#e8eef5; - --side:#0f1829;--row-bg:#1a2231;--row-bg-on:#22293b;--track:#1e2738; - --ok:#9dbfa8;--warn:#d9a55f;--err:#e0705c;--mag:#d9a55f; - --code-bg:#151e2a;--danger-bg:#3a1f1c;--warn-bg:#33291b; - --add:#9dbfa8;--add-bg:rgba(119,150,132,.22);--del:#e0705c;--del-bg:rgba(224,112,92,.16); + --accent:#a3c4ab;--accent-bg:rgba(132,169,140,.22);--on-accent:#0f172a;--on-err:#0f172a; + --accent-fill:#84a98c;--accent-soft:#84a98c; + --user-bg:#334259;--on-user:#e8eef5; + --side:#0f172a;--row-bg:#1a2334;--row-bg-on:#232c3f;--track:#232a3a; + --ok:#a3c4ab;--warn:#d9a55f;--err:#e0705c;--mag:#d9a55f; + --code-bg:#16202f;--danger-bg:#3a1f1c;--warn-bg:#33291b; + --add:#a3c4ab;--add-bg:rgba(132,169,140,.22);--del:#e0705c;--del-bg:rgba(224,112,92,.16); --shadow:none;--shadow-lift:0 4px 16px rgba(0,0,0,.35); --shadow-card:0 2px 12px rgba(0,0,0,.28),0 1px 3px rgba(0,0,0,.20)} /* Safari iOS peint un rectangle gris/noir sur TOUT élément touché (bouton, lien, @@ -1473,8 +1481,8 @@ html[data-files="1"] #files-btn{color:var(--accent)} l'écran dès la dixième discussion. */ .sidehead{flex:none;display:flex;align-items:center;gap:8px;margin:0; padding:14px 14px 10px;padding-top:calc(14px + env(safe-area-inset-top))} -#side-scroll{flex:1;min-height:0;overflow-y:auto;padding:0 12px 12px; - scrollbar-gutter:stable;display:flex;flex-direction:column;gap:2px} +#side-scroll{flex:1;min-height:0;overflow-y:auto;padding:0 0 12px; + scrollbar-gutter:stable;display:flex;flex-direction:column;gap:0} #side-perf{flex:none;border-top:1px solid var(--border);padding:12px 14px; padding-bottom:calc(12px + env(safe-area-inset-bottom))} @media (max-width:720px){ @@ -1525,12 +1533,13 @@ html[data-files="1"] #files-btn{color:var(--accent)} /* --- Discussions ----------------------------------------------------- */ /* La liste EST la barre latérale : titre à gauche, heure à droite, une ligne. Le reste (réglages) descend sous un unique repli. */ -#conv-search{width:100%;font:inherit;font-size:12.5px;color:var(--text); - background:var(--panel);border:1px solid var(--border);border-radius:10px; - padding:7px 11px;margin:0 0 6px} +#conv-search{width:calc(100% - 24px);font:inherit;font-size:12.5px;color:var(--text); + background:var(--panel);border:1px solid var(--border);border-radius:9px; + padding:7px 11px;margin:0 12px 8px} #conv-search::placeholder{color:var(--dim)} #conv-search:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)} -.side .preset{padding:8px 11px;margin:1px 0;gap:10px} +.side .preset{padding:11px 14px;margin:0;gap:10px;border-radius:0; + border:0;border-bottom:1px solid var(--border)} .side .preset .preset-name{font-size:13px;font-weight:450;white-space:nowrap;overflow:hidden; text-overflow:ellipsis;word-break:normal;overflow-wrap:normal} .side .preset.active .preset-name{font-weight:550} @@ -1551,25 +1560,21 @@ html[data-files="1"] #files-btn{color:var(--accent)} #side-settings>.sidegroup{margin-top:2px} /* --- Moniteur machine (pied de barre latérale) ----------------------- */ -.perf-head{display:flex;align-items:center;gap:8px;margin-bottom:9px; - font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:.1em;color:var(--dim)} -.perf-head .perf-toggle{margin-left:auto;border:0;background:transparent;color:var(--dim); - padding:0 2px;margin:0;font-size:11px;cursor:pointer;line-height:1} -.perf-row{margin:0 0 9px} +.perf-head{display:flex;align-items:center;gap:8px;margin-bottom:11px; + font-size:14px;font-weight:600;color:var(--text);letter-spacing:-.01em} +.perf-row{margin:0 0 14px} .perf-row:last-child{margin-bottom:0} -.perf-l{display:flex;align-items:baseline;gap:8px;font-size:11.5px;color:var(--dim);margin-bottom:4px} +.perf-l{display:flex;align-items:baseline;gap:8px;font-size:12.5px;color:var(--text);margin-bottom:6px} .perf-l b{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; - font-weight:450;color:var(--text)} -.perf-l .perf-pct{font-family:var(--mono);font-size:11px;color:var(--text);font-variant-numeric:tabular-nums} + font-weight:400;color:var(--text)} +.perf-l .perf-pct{font-size:12.5px;color:var(--text);font-variant-numeric:tabular-nums} /* La jauge : 4 px, coins ronds, remplissage sauge. Elle passe à l'ambre puis au rouille quand la machine sature — c'est là que l'information devient utile. */ -.perf-track{height:4px;border-radius:999px;background:var(--row-bg-on);overflow:hidden} +.perf-track{height:7px;border-radius:999px;background:var(--track);overflow:hidden} .perf-fill{height:100%;width:0;border-radius:999px;background:var(--accent-fill); transition:width .4s ease,background .4s ease} .perf-fill.hot{background:var(--warn)} .perf-fill.max{background:var(--err)} -.perf-sub{margin-top:3px;font-family:var(--mono);font-size:9.5px;color:var(--dim);opacity:.85} -html[data-perf="0"] .perf-body{display:none} /* --- Boutons : sans contour, micro-interaction au survol ------------- */ button{border-color:transparent;background:var(--row-bg);border-radius:9px; @@ -1582,13 +1587,13 @@ button:active:not(:disabled){transform:translateY(1px)} /* La maquette encadre la réponse de l'IA : sur un fond de fil coloré, du texte nu flotte sans ancrage, et la frontière entre deux réponses se perd. */ #chat{padding-top:22px} -#chat .msg.assistant{background:var(--bubble);border:1px solid var(--border); - border-radius:16px;padding:14px 18px;max-width:100%;box-shadow:var(--shadow-card)} +#chat .msg.assistant{background:var(--bubble);border:0; + border-radius:10px;padding:15px 17px;max-width:100%;box-shadow:var(--shadow-card)} #chat .msg.assistant.errmsg{background:var(--danger-bg);border-color:color-mix(in srgb,var(--err) 45%,transparent)} /* Question : aplat de sauge, coin bas-droit rentré. --on-accent garantit le contraste dans les DEUX variantes. */ -#chat .msg.user{background:var(--user-bg);color:var(--on-user);border-radius:18px 18px 6px 18px; - padding:11px 16px;max-width:75%;box-shadow:var(--shadow-card)} +#chat .msg.user{background:var(--user-bg);color:var(--on-user);border-radius:10px; + padding:13px 16px;max-width:62%;box-shadow:var(--shadow-card)} #chat .msg.user .label{color:var(--on-user);opacity:.75} #chat .msg.user .body a{color:var(--on-user);text-decoration:underline} #chat .msg.user code{background:rgba(255,255,255,.18);color:inherit} @@ -1627,8 +1632,9 @@ html[data-files="1"] #files-btn{color:var(--accent)} /* Les outils de la saisie (joindre, fichiers, envoyer) montent DANS la pilule, à droite du texte : c'est là qu'on les cherche, et le pied de carte redevient ce qu'il doit être — une jauge de contexte, rien d'autre. */ -#inputbar{align-items:flex-end;padding:8px 8px 8px 18px;gap:4px;box-shadow:var(--shadow-card)} -#composer:focus-within #inputbar,#composer:focus-within #ctxmeta{border-color:var(--accent-soft)} +#inputbar{align-items:flex-end;padding:8px 8px 8px 18px;gap:4px;box-shadow:var(--shadow-card); + border-radius:14px 14px 0 0} +#ctxmeta{border-radius:0 0 14px 14px} #ctxmeta{padding-bottom:12px} #ctxtrack{border-radius:999px} @@ -1661,7 +1667,7 @@ html[data-files="1"] #files-btn{color:var(--accent)} SURIMPRESSION à droite plutôt qu'en flux. En flux, elles réservaient leur largeur en permanence — invisibles mais présentes — et le titre était tronqué au tiers de la place disponible. */ -.side .preset{position:relative;border-radius:12px} +.side .preset{position:relative} .side .preset .conv-acts{position:absolute;right:6px;top:50%;transform:translateY(-50%); margin:0;background:var(--row-bg-on);border-radius:9px;padding:1px} .side .preset.active .conv-acts{background:var(--row-bg)} @@ -1707,3 +1713,53 @@ html[data-files="1"] #files-btn{color:var(--accent)} #chat .msg.assistant .body code{background:var(--code-bg); border:1px solid color-mix(in srgb,var(--text) 7%,transparent);border-radius:5px} #chat .msg.assistant .body pre code{border:0;background:transparent} +/* --- Titres de la barre latérale ------------------------------------ + La maquette n'a qu'un style de titre : casse normale, demi-gras, taille du + texte courant (« Performance »). Le monospace en capitales de la charte + précédente y jurait, et faisait crier des libellés qui n'ont rien à dire. */ +.side .stitle{font-family:var(--font);text-transform:none;letter-spacing:-.01em; + font-size:13.5px;font-weight:600;color:var(--text)} +#side-scroll>details>summary,#side-settings>summary{padding:0 14px} +#side-settings{margin-top:0;border-top:1px solid var(--border)} +#side-settings>.sidegroup{padding:0 12px} +/* La liste elle-même n'a pas de gouttière : ses lignes touchent les deux bords, + comme dans la maquette. Le filet du bas de la dernière ligne fait la + séparation avec ce qui suit. */ +#conv-list{margin:0} +.side .preset:hover{background:var(--row-bg)} +.side .preset .conv-acts{right:8px} +/* Message d'absence de matériel : même famille que les jauges, en plus discret. + (La sous-ligne chiffrée, elle, a quitté l'affichage — le détail est en + infobulle, comme dans la maquette.) */ +.perf-sub{font-size:11.5px;color:var(--dim)} +/* --- Carte de saisie : le trait sauge de la maquette ----------------- + C'est le seul contour coloré de l'écran, et il désigne l'endroit où l'on + écrit. ⚠️ Posé EN FIN DE FEUILLE : la règle de coque plus haut + (`#inputbar,#ctxmeta{border-color:var(--border)}`) reprenait sinon la main et + le cadre retombait au gris commun. + La flèche d'envoi prend la MÊME sauge que la maquette — c'est un aplat plein + de 18 px, pas du texte. */ +@media (min-width:721px){ + #inputbar,#ctxmeta{border-color:var(--accent-fill)} + #composer:focus-within #inputbar,#composer:focus-within #ctxmeta{border-color:var(--accent)} +} +#inputbar,#ctxmeta{border-color:var(--accent-fill)} +#send{color:var(--accent-fill)} +#send:hover:not(:disabled){color:var(--accent)} + +/* --- Échelle typographique des planches ------------------------------ + « Typography: Inter » — Bold 24px pour les titres, Regular 16px pour le corps, + Medium 14px pour le texte secondaire. On l'applique là où elle a un sens + (le fil, la barre latérale) plutôt qu'en gonflant toute l'interface : les + panneaux de réglages sont des formulaires denses, pas du texte de lecture. */ +#chat .msg .body{font-size:16px} +#chat .msg.user .body,#chat .msg.assistant .body{line-height:1.6} +#input{font-size:16px} +.side .preset .preset-name{font-size:14px} +.side .preset .conv-when{font-size:13px} +.perf-head{font-size:16px;font-weight:700} +.perf-l,.perf-l .perf-pct{font-size:14px;font-weight:500} +.side .stitle{font-size:14px;font-weight:600} +/* Marque : « Loki v0.9.4 » en tête de colonne, à l'échelle des titres. */ +#brand svg{height:20px} +#ver{font-size:13px;font-weight:500;margin-left:8px}