mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-12 01:37:06 +02:00
v0.5.7 : nouveau theme ChatGPT (noir et blanc, mise en page epuree) + composer en carte avec jauge de contexte integree
This commit is contained in:
1 parent
cdcf89b068
commit
d22e8c8622
9 files changed
+191
-13
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -3,13 +3,13 @@
|
||||
"FileVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 5,
|
||||
"Patch": 6,
|
||||
"Patch": 7,
|
||||
"Build": 0
|
||||
},
|
||||
"ProductVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 5,
|
||||
"Patch": 6,
|
||||
"Patch": 7,
|
||||
"Build": 0
|
||||
},
|
||||
"FileFlagsMask": "3f",
|
||||
@@ -25,7 +25,7 @@
|
||||
"LegalCopyright": "Copyright (c) 2026 Jean contributors. MIT License.",
|
||||
"OriginalFilename": "jean.exe",
|
||||
"ProductName": "Jean",
|
||||
"ProductVersion": "0.5.6",
|
||||
"ProductVersion": "0.5.7",
|
||||
"Comments": "https://github.com/nathaninline/jean — projet open source (MIT)"
|
||||
},
|
||||
"VarFileInfo": {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Version = "0.5.6"
|
||||
const Version = "0.5.7"
|
||||
|
||||
// Main est le vrai main() du binaire (cmd/jean ne fait que l'appeler).
|
||||
func Main() {
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
[data-theme="soft"]{color-scheme:light;--font:'Lexend',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--bubble:#c2611f;--bg:#faf8f4;--panel:#ffffff;--border:#e8e3d9;--text:#2c2a26;--dim:#8c8577;--accent:#c2611f;--accent-bg:rgba(194,97,31,.10);--ok:#c2611f;--warn:#b45309;--err:#dc2626;--mag:#9a5bd6;--code-bg:#f3f0ea;--danger-bg:#fbe9e7;--warn-bg:#fbf1e0}
|
||||
/* "doux sombre" — même esprit que "doux" (Lexend, tons chauds) sur fond sombre chaud. */
|
||||
[data-theme="soft-dark"]{color-scheme:dark;--font:'Lexend',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--bubble:#c2611f;--bg:#1c1917;--panel:#26221e;--border:#3a342c;--text:#ece7de;--dim:#9c9284;--accent:#e8965a;--accent-bg:rgba(232,150,90,.14);--ok:#e8965a;--warn:#d9a441;--err:#f07862;--mag:#c39be8;--code-bg:#171412;--danger-bg:#3d2420;--warn-bg:#3a2f1c}
|
||||
/* "ChatGPT" — monochrome (noir & blanc), police système, aucun accent coloré.
|
||||
Seules les erreurs gardent un rouge discret pour rester lisibles. */
|
||||
[data-theme="gpt"]{color-scheme:light;--font:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;--bubble:#e8e8e8;--bg:#ffffff;--panel:#f9f9f9;--border:#e3e3e3;--text:#0d0d0d;--dim:#8f8f8f;--accent:#0d0d0d;--accent-bg:rgba(0,0,0,.06);--ok:#0d0d0d;--warn:#5d5d5d;--err:#c0392b;--mag:#5d5d5d;--code-bg:#f4f4f4;--danger-bg:#fbeae8;--warn-bg:#f0f0f0}
|
||||
/* Bulle utilisateur claire : le texte blanc par défaut (.msg.user) serait illisible. */
|
||||
[data-theme="gpt"] .msg.user{color:var(--text)}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--font);background:var(--bg);color:var(--text);height:100vh;height:100dvh;display:flex;font-size:14px}
|
||||
/* PWA plein écran (viewport-fit=cover) : on décale le contenu des bords pour ne
|
||||
@@ -242,9 +247,13 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
/* Composer = un seul bloc cohérent : barre de contexte intégrée en tête (fine
|
||||
ligne de progression pleine largeur affleurant le bord) + zone de saisie. */
|
||||
#composer{border-top:1px solid var(--border);background:var(--panel-2,rgba(255,255,255,.02))}
|
||||
#ctxtrack{height:3px;width:100%;background:var(--panel);overflow:hidden}
|
||||
/* La jauge est un ENFANT de #ctxmeta (et non plus un frère au-dessus) : ainsi
|
||||
elle peut être clippée par la forme arrondie du bloc — indispensable au
|
||||
thème ChatGPT où elle sert de bord bas de carte. Position/rendu inchangés
|
||||
pour les autres thèmes : filet collé en haut de la ligne de contexte. */
|
||||
#ctxtrack{position:absolute;top:0;left:0;right:0;height:3px;background:var(--panel);overflow:hidden}
|
||||
#ctx-fill{height:100%;width:0%;background:var(--ok,#3a7);transition:width .3s,background .3s}
|
||||
#ctxmeta{display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:5px 12px 0}
|
||||
#ctxmeta{position:relative;display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:8px 12px 0}
|
||||
#ctx-text{font-size:10px;letter-spacing:.03em;white-space:nowrap}
|
||||
#ctx-compact{display:none;margin:0;padding:2px 10px;font-size:10px;background:var(--warn-bg);border-color:var(--warn,#c93)}
|
||||
#inputbar{padding:8px 12px;display:flex;gap:8px}
|
||||
@@ -479,6 +488,81 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
espacement vertical (le conteneur n'est pas un flex à gap) → ils se touchaient.
|
||||
On aligne leur rythme sur celui du reste du formulaire (.mform). */
|
||||
#mcp-stdio-fields,#mcp-http-fields{display:flex;flex-direction:column;gap:14px}
|
||||
|
||||
/* ===== Thème "ChatGPT" — mise en page ==============================
|
||||
Les thèmes gpt-* ne changent pas que la palette : ils reprennent la
|
||||
disposition de ChatGPT — colonne de lecture centrée, réponses SANS bulle
|
||||
(texte nu sur le fond), question de l'utilisateur en pastille arrondie à
|
||||
droite, composer en « pilule », pas d'étiquettes VOUS/JEAN, typo système
|
||||
plus grande et plus aérée. Tout est préfixé [data-theme="gpt"] : aucun
|
||||
autre thème n'est affecté. */
|
||||
[data-theme="gpt"] body{font-size:15px}
|
||||
[data-theme="gpt"] h1{color:var(--text);font-weight:600;letter-spacing:-.01em}
|
||||
/* Barre latérale : plus sombre/atténuée que la zone de conversation. */
|
||||
[data-theme="gpt"] .side{background:var(--panel);border-right-color:transparent}
|
||||
@media (max-width:720px){[data-theme="gpt"] .side{background:var(--panel)}}
|
||||
/* Colonne de lecture centrée (comme le fil ChatGPT), pleine largeur sur mobile. */
|
||||
[data-theme="gpt"] #chat{padding:28px max(16px,calc(50% - 380px)) 8px;gap:22px}
|
||||
/* Réponses de l'assistant : pas de cadre, pas de fond — juste du texte. */
|
||||
[data-theme="gpt"] #chat .msg.assistant{background:transparent;border:none;padding:0;max-width:100%;line-height:1.7}
|
||||
/* Question : pastille arrondie à droite, texte lisible sur fond clair OU sombre. */
|
||||
[data-theme="gpt"] #chat .msg.user{background:var(--bubble);color:var(--text);border-radius:20px;padding:11px 18px;max-width:75%;line-height:1.6}
|
||||
[data-theme="gpt"] #chat .msg.user .label{color:var(--dim)}
|
||||
/* Étiquettes VOUS / JEAN : inutiles ici (l'alignement suffit), sauf pour les
|
||||
bulles repliables (raisonnement / outils) où le label est le bouton de repli. */
|
||||
[data-theme="gpt"] #chat .msg .label{display:none}
|
||||
[data-theme="gpt"] #chat .msg.collapsible .label{display:block}
|
||||
/* Raisonnement / outils : encart gris discret plutôt qu'un cadre pointillé. */
|
||||
[data-theme="gpt"] #chat .msg.reasoning,[data-theme="gpt"] #chat .msg.tool{background:var(--panel);border:1px solid var(--border);border-radius:14px;color:var(--dim);padding:12px 16px}
|
||||
[data-theme="gpt"] #chat .msg.reasoning{color:var(--dim)}
|
||||
[data-theme="gpt"] #chat .msg.tool .tool-head{color:var(--text)}
|
||||
[data-theme="gpt"] #chat .msg.assistant .body pre,[data-theme="gpt"] #chat .msg.reasoning .body pre,[data-theme="gpt"] #chat .msg.tool pre{border-radius:12px;padding:12px 14px}
|
||||
[data-theme="gpt"] #chat .msg.assistant .body a{text-decoration:underline;text-underline-offset:2px}
|
||||
/* Composer = UNE seule carte posée PAR-DESSUS le fil : la conversation
|
||||
continue de défiler DERRIÈRE elle et s'efface en fondu (dégradé vers le fond)
|
||||
au lieu de s'arrêter net au-dessus. Dans la carte, de haut en bas : la saisie
|
||||
et ses boutons, un pied discret (préréglage · contexte · compacter), puis la
|
||||
jauge de contexte qui FORME le bord bas — elle épouse les coins arrondis
|
||||
(overflow:hidden sur le rayon). Les trois blocs sont des frères dans le HTML :
|
||||
on les recolle en dessinant les bordures par morceaux et l'ordre visuel vient
|
||||
de l'ordre flex — aucun changement de markup. */
|
||||
[data-theme="gpt"] #composer{position:absolute;left:0;right:0;bottom:0;z-index:4;border-top:none;display:flex;flex-direction:column;padding:34px 0 8px;--col:max(16px,calc(50% - 380px));background:linear-gradient(to bottom,transparent 0,var(--bg) 34%)}
|
||||
[data-theme="gpt"] #chat{padding-bottom:170px}
|
||||
[data-theme="gpt"] #scrollbtn{bottom:180px;border-radius:999px}
|
||||
[data-theme="gpt"] #compact-banner{order:0;margin:0 var(--col) 8px;border-radius:14px}
|
||||
[data-theme="gpt"] #inputbar{order:1;margin:0 var(--col);padding:8px 8px 8px 16px;gap:8px;align-items:flex-end;background:var(--panel);border:1px solid var(--border);border-bottom:none;border-radius:26px 26px 0 0}
|
||||
[data-theme="gpt"] #input{background:transparent;border:none;border-radius:0;padding:9px 0;min-height:26px}
|
||||
/* Pas de cadre qui s'assombrit au focus : la carte garde exactement la même
|
||||
bordure, seule la saisie change (curseur). */
|
||||
[data-theme="gpt"] #input:focus{border-color:transparent}
|
||||
[data-theme="gpt"] #send,[data-theme="gpt"] #stop{border-radius:999px;height:36px;min-height:36px;padding:0 18px;margin:0}
|
||||
[data-theme="gpt"] #send{background:var(--text);color:var(--bg);border-color:var(--text);font-weight:600}
|
||||
[data-theme="gpt"] #send:hover:not(:disabled){opacity:.85;color:var(--bg);border-color:var(--text)}
|
||||
/* Pied de carte : c'est LUI qui porte l'arrondi bas (26 px, comme en haut) et
|
||||
qui clippe la jauge. Un bandeau de 7 px ne peut pas dessiner un rayon de 26 px
|
||||
(le navigateur l'écrase, d'où les coins bizarres) : on laisse donc la carte
|
||||
découper la jauge dans sa propre forme — la barre est bien épaisse ET rognée
|
||||
exactement sur la courbe, symétrique du haut. */
|
||||
[data-theme="gpt"] #ctxmeta{order:2;margin:0 var(--col);padding:6px 16px 15px;gap:10px;background:var(--panel);border:1px solid var(--border);border-top:none;border-radius:0 0 26px 26px;overflow:hidden}
|
||||
[data-theme="gpt"] #ctx-text,[data-theme="gpt"] #status-preset{color:var(--dim);font-size:10px;letter-spacing:.02em}
|
||||
[data-theme="gpt"] #ctx-compact{border-radius:999px;padding:2px 10px;font-size:10px;background:transparent;border-color:var(--border);color:var(--dim)}
|
||||
[data-theme="gpt"] #ctx-compact:hover{border-color:var(--dim);color:var(--text)}
|
||||
/* Jauge = bord bas de la carte : épaisse, plaquée au ras du bord, rognée par
|
||||
l'arrondi du pied de carte. */
|
||||
[data-theme="gpt"] #ctxtrack{top:auto;bottom:0;height:7px;background:var(--border)}
|
||||
[data-theme="gpt"] #ctx-fill{background:var(--text);opacity:.75}
|
||||
[data-theme="gpt"] #sendhint{order:4;padding:8px var(--col) 0}
|
||||
/* Reste de l'UI : coins arrondis et contrastes doux, dans le même esprit. */
|
||||
[data-theme="gpt"] button,[data-theme="gpt"] .addbtn,[data-theme="gpt"] .mem-search,[data-theme="gpt"] #theme-select{border-radius:10px}
|
||||
[data-theme="gpt"] .preset,[data-theme="gpt"] .gated-block,[data-theme="gpt"] .lc-mode{border-radius:12px}
|
||||
[data-theme="gpt"] .preset{background:transparent;border-color:transparent}
|
||||
[data-theme="gpt"] .preset:hover{background:var(--accent-bg);border-color:transparent}
|
||||
[data-theme="gpt"] .preset.active{background:var(--accent-bg);border-color:var(--border);color:var(--text)}
|
||||
[data-theme="gpt"] .subhead,[data-theme="gpt"] .lc-reco,[data-theme="gpt"] .lc-mode-go,[data-theme="gpt"] .lc-mode-active-tag{color:var(--dim)}
|
||||
[data-theme="gpt"] button:hover{border-color:var(--dim);color:var(--text)}
|
||||
[data-theme="gpt"] .switch input:checked + .slider{background:var(--text)}
|
||||
[data-theme="gpt"] .switch .slider::before{background:var(--bg)}
|
||||
[data-theme="gpt"] .switch input:not(:checked) + .slider::before{background:#fff}
|
||||
</style></head><body>
|
||||
<button id="menubtn" onclick="toggleSide()">☰</button>
|
||||
<div id="backdrop" onclick="toggleSide()"></div>
|
||||
@@ -670,8 +754,8 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
<button id="scrollbtn" onclick="jumpBottom()" title="aller en bas">↓</button>
|
||||
<div id="composer">
|
||||
<div id="compact-banner" style="display:none"><span class="cspin">⏳</span> Compactage du contexte en cours — résumé des anciens tours…</div>
|
||||
<div id="ctxtrack"><div id="ctx-fill"></div></div>
|
||||
<div id="ctxmeta">
|
||||
<div id="ctxtrack"><div id="ctx-fill"></div></div>
|
||||
<span id="status-preset" class="muted" style="font-size:10px;letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"></span>
|
||||
<span style="display:flex;align-items:center;gap:10px;margin-left:auto">
|
||||
<span id="ctx-text" class="muted">contexte 0 / 0</span>
|
||||
@@ -977,7 +1061,9 @@ async function loadSys(){
|
||||
// Registre des thèmes disponibles. Pour en AJOUTER un : ajouter un bloc
|
||||
// [data-theme="…"] dans le <style> puis une entrée {id,label} ici — le reste
|
||||
// (menu déroulant, persistance localStorage, application) est automatique.
|
||||
const THEMES=[{id:'dark',label:'sombre'},{id:'light',label:'clair'},{id:'soft',label:'doux'},{id:'soft-dark',label:'doux sombre'}];
|
||||
const THEMES=[{id:'dark',label:'sombre'},{id:'light',label:'clair'},{id:'soft',label:'doux'},{id:'soft-dark',label:'doux sombre'},{id:'gpt',label:'ChatGPT'}];
|
||||
// Anciens identifiants (v0.5.7, thème livré en deux variantes) → thème unique.
|
||||
const THEME_ALIASES={'gpt-light':'gpt','gpt-dark':'gpt'};
|
||||
// Lexend (thème "doux") n'est chargé QUE si ce thème est choisi : aucune requête
|
||||
// externe (Google Fonts) tant qu'on reste en sombre/clair — jean reste local par
|
||||
// défaut. Fallback système sans-serif si hors ligne.
|
||||
@@ -989,6 +1075,7 @@ function ensureSoftFont(){
|
||||
document.head.appendChild(l);
|
||||
}
|
||||
function applyTheme(id){
|
||||
id=THEME_ALIASES[id]||id;
|
||||
if(!THEMES.some(t=>t.id===id)) id='dark';
|
||||
if(id==='soft'||id==='soft-dark') ensureSoftFont();
|
||||
document.documentElement.setAttribute('data-theme', id);
|
||||
|
||||
@@ -209,8 +209,8 @@
|
||||
<button id="scrollbtn" onclick="jumpBottom()" title="aller en bas">↓</button>
|
||||
<div id="composer">
|
||||
<div id="compact-banner" style="display:none"><span class="cspin">⏳</span> Compactage du contexte en cours — résumé des anciens tours…</div>
|
||||
<div id="ctxtrack"><div id="ctx-fill"></div></div>
|
||||
<div id="ctxmeta">
|
||||
<div id="ctxtrack"><div id="ctx-fill"></div></div>
|
||||
<span id="status-preset" class="muted" style="font-size:10px;letter-spacing:.03em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis"></span>
|
||||
<span style="display:flex;align-items:center;gap:10px;margin-left:auto">
|
||||
<span id="ctx-text" class="muted">contexte 0 / 0</span>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Registre des thèmes disponibles. Pour en AJOUTER un : ajouter un bloc
|
||||
// [data-theme="…"] dans le <style> puis une entrée {id,label} ici — le reste
|
||||
// (menu déroulant, persistance localStorage, application) est automatique.
|
||||
const THEMES=[{id:'dark',label:'sombre'},{id:'light',label:'clair'},{id:'soft',label:'doux'},{id:'soft-dark',label:'doux sombre'}];
|
||||
const THEMES=[{id:'dark',label:'sombre'},{id:'light',label:'clair'},{id:'soft',label:'doux'},{id:'soft-dark',label:'doux sombre'},{id:'gpt',label:'ChatGPT'}];
|
||||
// Anciens identifiants (v0.5.7, thème livré en deux variantes) → thème unique.
|
||||
const THEME_ALIASES={'gpt-light':'gpt','gpt-dark':'gpt'};
|
||||
// Lexend (thème "doux") n'est chargé QUE si ce thème est choisi : aucune requête
|
||||
// externe (Google Fonts) tant qu'on reste en sombre/clair — jean reste local par
|
||||
// défaut. Fallback système sans-serif si hors ligne.
|
||||
@@ -14,6 +16,7 @@ function ensureSoftFont(){
|
||||
document.head.appendChild(l);
|
||||
}
|
||||
function applyTheme(id){
|
||||
id=THEME_ALIASES[id]||id;
|
||||
if(!THEMES.some(t=>t.id===id)) id='dark';
|
||||
if(id==='soft'||id==='soft-dark') ensureSoftFont();
|
||||
document.documentElement.setAttribute('data-theme', id);
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
[data-theme="soft"]{color-scheme:light;--font:'Lexend',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--bubble:#c2611f;--bg:#faf8f4;--panel:#ffffff;--border:#e8e3d9;--text:#2c2a26;--dim:#8c8577;--accent:#c2611f;--accent-bg:rgba(194,97,31,.10);--ok:#c2611f;--warn:#b45309;--err:#dc2626;--mag:#9a5bd6;--code-bg:#f3f0ea;--danger-bg:#fbe9e7;--warn-bg:#fbf1e0}
|
||||
/* "doux sombre" — même esprit que "doux" (Lexend, tons chauds) sur fond sombre chaud. */
|
||||
[data-theme="soft-dark"]{color-scheme:dark;--font:'Lexend',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--bubble:#c2611f;--bg:#1c1917;--panel:#26221e;--border:#3a342c;--text:#ece7de;--dim:#9c9284;--accent:#e8965a;--accent-bg:rgba(232,150,90,.14);--ok:#e8965a;--warn:#d9a441;--err:#f07862;--mag:#c39be8;--code-bg:#171412;--danger-bg:#3d2420;--warn-bg:#3a2f1c}
|
||||
/* "ChatGPT" — monochrome (noir & blanc), police système, aucun accent coloré.
|
||||
Seules les erreurs gardent un rouge discret pour rester lisibles. */
|
||||
[data-theme="gpt"]{color-scheme:light;--font:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;--bubble:#e8e8e8;--bg:#ffffff;--panel:#f9f9f9;--border:#e3e3e3;--text:#0d0d0d;--dim:#8f8f8f;--accent:#0d0d0d;--accent-bg:rgba(0,0,0,.06);--ok:#0d0d0d;--warn:#5d5d5d;--err:#c0392b;--mag:#5d5d5d;--code-bg:#f4f4f4;--danger-bg:#fbeae8;--warn-bg:#f0f0f0}
|
||||
/* Bulle utilisateur claire : le texte blanc par défaut (.msg.user) serait illisible. */
|
||||
[data-theme="gpt"] .msg.user{color:var(--text)}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--font);background:var(--bg);color:var(--text);height:100vh;height:100dvh;display:flex;font-size:14px}
|
||||
/* PWA plein écran (viewport-fit=cover) : on décale le contenu des bords pour ne
|
||||
@@ -223,9 +228,13 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
/* Composer = un seul bloc cohérent : barre de contexte intégrée en tête (fine
|
||||
ligne de progression pleine largeur affleurant le bord) + zone de saisie. */
|
||||
#composer{border-top:1px solid var(--border);background:var(--panel-2,rgba(255,255,255,.02))}
|
||||
#ctxtrack{height:3px;width:100%;background:var(--panel);overflow:hidden}
|
||||
/* La jauge est un ENFANT de #ctxmeta (et non plus un frère au-dessus) : ainsi
|
||||
elle peut être clippée par la forme arrondie du bloc — indispensable au
|
||||
thème ChatGPT où elle sert de bord bas de carte. Position/rendu inchangés
|
||||
pour les autres thèmes : filet collé en haut de la ligne de contexte. */
|
||||
#ctxtrack{position:absolute;top:0;left:0;right:0;height:3px;background:var(--panel);overflow:hidden}
|
||||
#ctx-fill{height:100%;width:0%;background:var(--ok,#3a7);transition:width .3s,background .3s}
|
||||
#ctxmeta{display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:5px 12px 0}
|
||||
#ctxmeta{position:relative;display:flex;align-items:center;justify-content:flex-end;gap:10px;padding:8px 12px 0}
|
||||
#ctx-text{font-size:10px;letter-spacing:.03em;white-space:nowrap}
|
||||
#ctx-compact{display:none;margin:0;padding:2px 10px;font-size:10px;background:var(--warn-bg);border-color:var(--warn,#c93)}
|
||||
#inputbar{padding:8px 12px;display:flex;gap:8px}
|
||||
@@ -460,3 +469,78 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
espacement vertical (le conteneur n'est pas un flex à gap) → ils se touchaient.
|
||||
On aligne leur rythme sur celui du reste du formulaire (.mform). */
|
||||
#mcp-stdio-fields,#mcp-http-fields{display:flex;flex-direction:column;gap:14px}
|
||||
|
||||
/* ===== Thème "ChatGPT" — mise en page ==============================
|
||||
Les thèmes gpt-* ne changent pas que la palette : ils reprennent la
|
||||
disposition de ChatGPT — colonne de lecture centrée, réponses SANS bulle
|
||||
(texte nu sur le fond), question de l'utilisateur en pastille arrondie à
|
||||
droite, composer en « pilule », pas d'étiquettes VOUS/JEAN, typo système
|
||||
plus grande et plus aérée. Tout est préfixé [data-theme="gpt"] : aucun
|
||||
autre thème n'est affecté. */
|
||||
[data-theme="gpt"] body{font-size:15px}
|
||||
[data-theme="gpt"] h1{color:var(--text);font-weight:600;letter-spacing:-.01em}
|
||||
/* Barre latérale : plus sombre/atténuée que la zone de conversation. */
|
||||
[data-theme="gpt"] .side{background:var(--panel);border-right-color:transparent}
|
||||
@media (max-width:720px){[data-theme="gpt"] .side{background:var(--panel)}}
|
||||
/* Colonne de lecture centrée (comme le fil ChatGPT), pleine largeur sur mobile. */
|
||||
[data-theme="gpt"] #chat{padding:28px max(16px,calc(50% - 380px)) 8px;gap:22px}
|
||||
/* Réponses de l'assistant : pas de cadre, pas de fond — juste du texte. */
|
||||
[data-theme="gpt"] #chat .msg.assistant{background:transparent;border:none;padding:0;max-width:100%;line-height:1.7}
|
||||
/* Question : pastille arrondie à droite, texte lisible sur fond clair OU sombre. */
|
||||
[data-theme="gpt"] #chat .msg.user{background:var(--bubble);color:var(--text);border-radius:20px;padding:11px 18px;max-width:75%;line-height:1.6}
|
||||
[data-theme="gpt"] #chat .msg.user .label{color:var(--dim)}
|
||||
/* Étiquettes VOUS / JEAN : inutiles ici (l'alignement suffit), sauf pour les
|
||||
bulles repliables (raisonnement / outils) où le label est le bouton de repli. */
|
||||
[data-theme="gpt"] #chat .msg .label{display:none}
|
||||
[data-theme="gpt"] #chat .msg.collapsible .label{display:block}
|
||||
/* Raisonnement / outils : encart gris discret plutôt qu'un cadre pointillé. */
|
||||
[data-theme="gpt"] #chat .msg.reasoning,[data-theme="gpt"] #chat .msg.tool{background:var(--panel);border:1px solid var(--border);border-radius:14px;color:var(--dim);padding:12px 16px}
|
||||
[data-theme="gpt"] #chat .msg.reasoning{color:var(--dim)}
|
||||
[data-theme="gpt"] #chat .msg.tool .tool-head{color:var(--text)}
|
||||
[data-theme="gpt"] #chat .msg.assistant .body pre,[data-theme="gpt"] #chat .msg.reasoning .body pre,[data-theme="gpt"] #chat .msg.tool pre{border-radius:12px;padding:12px 14px}
|
||||
[data-theme="gpt"] #chat .msg.assistant .body a{text-decoration:underline;text-underline-offset:2px}
|
||||
/* Composer = UNE seule carte posée PAR-DESSUS le fil : la conversation
|
||||
continue de défiler DERRIÈRE elle et s'efface en fondu (dégradé vers le fond)
|
||||
au lieu de s'arrêter net au-dessus. Dans la carte, de haut en bas : la saisie
|
||||
et ses boutons, un pied discret (préréglage · contexte · compacter), puis la
|
||||
jauge de contexte qui FORME le bord bas — elle épouse les coins arrondis
|
||||
(overflow:hidden sur le rayon). Les trois blocs sont des frères dans le HTML :
|
||||
on les recolle en dessinant les bordures par morceaux et l'ordre visuel vient
|
||||
de l'ordre flex — aucun changement de markup. */
|
||||
[data-theme="gpt"] #composer{position:absolute;left:0;right:0;bottom:0;z-index:4;border-top:none;display:flex;flex-direction:column;padding:34px 0 8px;--col:max(16px,calc(50% - 380px));background:linear-gradient(to bottom,transparent 0,var(--bg) 34%)}
|
||||
[data-theme="gpt"] #chat{padding-bottom:170px}
|
||||
[data-theme="gpt"] #scrollbtn{bottom:180px;border-radius:999px}
|
||||
[data-theme="gpt"] #compact-banner{order:0;margin:0 var(--col) 8px;border-radius:14px}
|
||||
[data-theme="gpt"] #inputbar{order:1;margin:0 var(--col);padding:8px 8px 8px 16px;gap:8px;align-items:flex-end;background:var(--panel);border:1px solid var(--border);border-bottom:none;border-radius:26px 26px 0 0}
|
||||
[data-theme="gpt"] #input{background:transparent;border:none;border-radius:0;padding:9px 0;min-height:26px}
|
||||
/* Pas de cadre qui s'assombrit au focus : la carte garde exactement la même
|
||||
bordure, seule la saisie change (curseur). */
|
||||
[data-theme="gpt"] #input:focus{border-color:transparent}
|
||||
[data-theme="gpt"] #send,[data-theme="gpt"] #stop{border-radius:999px;height:36px;min-height:36px;padding:0 18px;margin:0}
|
||||
[data-theme="gpt"] #send{background:var(--text);color:var(--bg);border-color:var(--text);font-weight:600}
|
||||
[data-theme="gpt"] #send:hover:not(:disabled){opacity:.85;color:var(--bg);border-color:var(--text)}
|
||||
/* Pied de carte : c'est LUI qui porte l'arrondi bas (26 px, comme en haut) et
|
||||
qui clippe la jauge. Un bandeau de 7 px ne peut pas dessiner un rayon de 26 px
|
||||
(le navigateur l'écrase, d'où les coins bizarres) : on laisse donc la carte
|
||||
découper la jauge dans sa propre forme — la barre est bien épaisse ET rognée
|
||||
exactement sur la courbe, symétrique du haut. */
|
||||
[data-theme="gpt"] #ctxmeta{order:2;margin:0 var(--col);padding:6px 16px 15px;gap:10px;background:var(--panel);border:1px solid var(--border);border-top:none;border-radius:0 0 26px 26px;overflow:hidden}
|
||||
[data-theme="gpt"] #ctx-text,[data-theme="gpt"] #status-preset{color:var(--dim);font-size:10px;letter-spacing:.02em}
|
||||
[data-theme="gpt"] #ctx-compact{border-radius:999px;padding:2px 10px;font-size:10px;background:transparent;border-color:var(--border);color:var(--dim)}
|
||||
[data-theme="gpt"] #ctx-compact:hover{border-color:var(--dim);color:var(--text)}
|
||||
/* Jauge = bord bas de la carte : épaisse, plaquée au ras du bord, rognée par
|
||||
l'arrondi du pied de carte. */
|
||||
[data-theme="gpt"] #ctxtrack{top:auto;bottom:0;height:7px;background:var(--border)}
|
||||
[data-theme="gpt"] #ctx-fill{background:var(--text);opacity:.75}
|
||||
[data-theme="gpt"] #sendhint{order:4;padding:8px var(--col) 0}
|
||||
/* Reste de l'UI : coins arrondis et contrastes doux, dans le même esprit. */
|
||||
[data-theme="gpt"] button,[data-theme="gpt"] .addbtn,[data-theme="gpt"] .mem-search,[data-theme="gpt"] #theme-select{border-radius:10px}
|
||||
[data-theme="gpt"] .preset,[data-theme="gpt"] .gated-block,[data-theme="gpt"] .lc-mode{border-radius:12px}
|
||||
[data-theme="gpt"] .preset{background:transparent;border-color:transparent}
|
||||
[data-theme="gpt"] .preset:hover{background:var(--accent-bg);border-color:transparent}
|
||||
[data-theme="gpt"] .preset.active{background:var(--accent-bg);border-color:var(--border);color:var(--text)}
|
||||
[data-theme="gpt"] .subhead,[data-theme="gpt"] .lc-reco,[data-theme="gpt"] .lc-mode-go,[data-theme="gpt"] .lc-mode-active-tag{color:var(--dim)}
|
||||
[data-theme="gpt"] button:hover{border-color:var(--dim);color:var(--text)}
|
||||
[data-theme="gpt"] .switch input:checked + .slider{background:var(--text)}
|
||||
[data-theme="gpt"] .switch .slider::before{background:var(--bg)}
|
||||
[data-theme="gpt"] .switch input:not(:checked) + .slider::before{background:#fff}
|
||||
@@ -22,7 +22,11 @@ var webPrefsMu sync.Mutex
|
||||
// webPrefsAllowed liste les clés de préférence acceptées et, pour chacune, les
|
||||
// valeurs valides. On ne stocke que ce qui est connu (pas de champ libre).
|
||||
var webPrefsAllowed = map[string]map[string]bool{
|
||||
"theme": {"dark": true, "light": true, "soft": true, "soft-dark": true},
|
||||
// ⚠️ Tout thème ajouté à l'UI (registre THEMES, js/01-theme.js) doit être
|
||||
// listé ICI : sinon POST /api/prefs le rejette silencieusement, le serveur
|
||||
// conserve l'ancienne valeur et loadPrefs() la réapplique à chaque
|
||||
// rafraîchissement — le thème « saute » alors qu'il est bien en localStorage.
|
||||
"theme": {"dark": true, "light": true, "soft": true, "soft-dark": true, "gpt": true},
|
||||
"display": {"full": true, "simple": true},
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user