From 82136d6e0def06d10eca4b7c1b3334e84139d3bb Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 16 Aug 2026 19:03:08 +0000 Subject: [PATCH] =?UTF-8?q?Palettes=20officielles=20Stitch,=20g=C3=A9om?= =?UTF-8?q?=C3=A9trie=20et=20moniteur=20relev=C3=A9s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le projet Stitch porte deux planches que je n'avais pas vues — « Official Palette for Loki AI v1 » et « Official Dark Mode Palette for Loki AI ». Leurs couleurs NOMMÉES font désormais foi, et elles corrigent le relevé précédent, que la compression JPEG des captures avait décalé : clair Main BG #F5F6F8 · Sidebar BG #ECECEC · Primary Accent #95A899 Typography #333333 sombre Deep Navy #0F172A · Slate Surface #1E293B · Sage Accent #84A98C Le thème sombre retrouve donc exactement les deux teintes du cahier des charges, que mon relevé sur capture avait fait dériver vers #1E2834/#253141. Les surfaces non nommées (carte de réponse, carte de saisie, piste de jauge, bulle sombre) restent relevées sur les écrans de la maquette, en pleine résolution cette fois. Géométrie, elle aussi mesurée sur la maquette : - Bulles et carte de réponse : rayon 10 px (contre 16/18 et un coin rentré), la carte sans cadre — c'est l'ombre qui la détache. - Carte de saisie : rayon 14 px et CADRE SAUGE, le seul contour coloré de l'écran. Il retombait au gris commun, une règle de coque plus bas dans la feuille reprenant la main sur la couleur. - Liste des discussions : lignes pleine largeur séparées d'un filet, bande plate pour l'active — la maquette n'a ni pastille arrondie ni gouttière. - Moniteur machine : titre en casse normale (le monospace capitales de la charte précédente y criait), libellé + pourcentage à la même échelle, jauge de 7 px entièrement arrondie. La sous-ligne chiffrée passe en infobulle et le bouton de repli disparaît : la maquette n'en a pas. - Une jauge de VRAM par carte, en plus de la charge : sur une machine à une seule carte on retombe sur les trois jauges de la maquette (GPU, VRAM, RAM), et pour du LLM c'est la VRAM qui décide si un modèle tient. - Échelle typographique des planches : corps 16 px dans le fil et la saisie, secondaire 14 px medium dans la barre latérale. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd --- internal/loki/ui/index.html | 197 ++++++++++++++++----------- internal/loki/ui/src/index.tmpl.html | 6 +- internal/loki/ui/src/js/05-status.js | 19 +-- internal/loki/ui/src/js/18-shell.js | 20 --- internal/loki/ui/src/styles.css | 152 ++++++++++++++------- 5 files changed, 232 insertions(+), 162 deletions(-) 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}