mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-20 13:38:11 +02:00
v0.7.11 : le menu parle enfin la meme langue partout
This commit is contained in:
1 parent
fd758ce4f8
commit
f3769299b9
16 files changed
+961
-411
No files matched your search
@@ -4,7 +4,11 @@
|
||||
passent par ces variables ; rien d'autre ne code une couleur en dur. */
|
||||
:root{color-scheme:light;--font:-apple-system,BlinkMacSystemFont,'Segoe UI',Inter,Helvetica,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;--bubble:#f0f0f0;--bg:#ffffff;--panel:#fafafa;--border:#e0e0e2;--text:#0a0a0a;--dim:#6b6b70;--accent:#0a0a0a;--accent-bg:rgba(0,0,0,.05);--ok:#0a0a0a;--warn:#6b6b6b;--err:#c0392b;--mag:#6b6b6b;--code-bg:#f5f5f5;--danger-bg:#fbeae8;--warn-bg:#f2f2f2;--add:#1a7f37;--add-bg:rgba(26,127,55,.10);--del:#cf222e;--del-bg:rgba(207,34,46,.10)}
|
||||
[data-theme="dark"]{color-scheme:dark;--bubble:#232326;--bg:#0b0b0c;--panel:#161618;--border:#33333a;--text:#f2f2f4;--dim:#a0a0a8;--accent:#f5f5f5;--accent-bg:rgba(255,255,255,.07);--ok:#f5f5f5;--warn:#8e8e8e;--err:#f2777a;--mag:#8e8e8e;--code-bg:#000000;--danger-bg:#2e1a19;--warn-bg:#1c1c1c;--add:#4ac26b;--add-bg:rgba(74,194,107,.13);--del:#f85149;--del-bg:rgba(248,81,73,.13)}
|
||||
*{box-sizing:border-box}
|
||||
/* Safari iOS peint un rectangle gris/noir sur TOUT élément touché (bouton, lien,
|
||||
résumé de section, bulle repliable…). C'est un retour tactile par défaut, hors
|
||||
de notre charte et impossible à styler : on le coupe. Nos états :active/:focus
|
||||
restent, eux, gérés élément par élément. */
|
||||
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
|
||||
/* ⚠️ overflow:hidden sur html/body est INTERDIT EN PWA (tenté puis retiré le
|
||||
2026-07-28) : en PWA iOS, si le document ne peut plus défiler, WebKit décale
|
||||
TOUTE la mise en page à l'ouverture du clavier pour montrer le champ actif —
|
||||
@@ -109,18 +113,18 @@ h1{margin:0;font-size:18px;color:var(--accent)}
|
||||
.statuspill.loading .dot{animation:pulse 1s ease-in-out infinite}
|
||||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
|
||||
.statuspill .dot{width:7px;height:7px;border-radius:50%;background:currentColor}
|
||||
.statuspill .port{opacity:.6;font-weight:400}
|
||||
/* en-tête + bouton d'ajout de skill (toujours visible) */
|
||||
.skillshead{display:flex;align-items:center;justify-content:space-between;margin:12px 0 5px}
|
||||
.skillshead>span{font-size:11.5px;color:var(--dim);font-weight:600}
|
||||
.addbtn{font-size:11px;padding:3px 11px 4px;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:6px;cursor:pointer;line-height:1.2}
|
||||
.addbtn:hover{border-color:var(--accent);color:var(--accent)}
|
||||
.addbtn{font-size:11px;padding:4px 11px;background:var(--panel);color:var(--dim);border:1px solid var(--border);border-radius:999px;cursor:pointer;line-height:1.3;letter-spacing:.02em}
|
||||
.addbtn:hover{border-color:var(--dim);color:var(--text);background:var(--accent-bg)}
|
||||
.mem-count{font-size:10px;color:var(--dim);font-weight:400;letter-spacing:0;text-transform:none;font-family:var(--font);margin-left:4px}
|
||||
/* Titre de sous-section (Mémoire / Accès internet) : simple libellé, PAS de trait
|
||||
(le trait est réservé aux sections principales — cf. `details`). La séparation
|
||||
visuelle vient de la carte englobante .gated-block. */
|
||||
visuelle vient de la carte de lignes qui suit (.slist). */
|
||||
/* Barre repliable « Pages » : un seul chevron, bouton + ajouter aligné à droite. */
|
||||
.pages-bar{display:flex;align-items:center;gap:8px;cursor:pointer;user-select:none;padding:7px 0;margin-top:4px}
|
||||
.pages-bar .pages-caret{width:9px;text-align:center}
|
||||
.pages-bar .pages-caret{flex-shrink:0;font-size:9px;color:var(--dim);transition:transform .12s}
|
||||
.pages-bar.open .pages-caret{transform:rotate(90deg)}
|
||||
.pages-bar .pages-title{font-size:11.5px;color:var(--dim);font-weight:600}
|
||||
@@ -128,11 +132,9 @@ h1{margin:0;font-size:18px;color:var(--accent)}
|
||||
.mem-search{width:100%;box-sizing:border-box;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:5px 9px;font:inherit;font-size:12px;margin:0 0 6px}
|
||||
.mem-search:focus{outline:none;border-color:var(--accent)}
|
||||
.mem-list{max-height:240px;overflow-y:auto;overscroll-behavior:contain}
|
||||
.mem-list .preset{margin:3px 0;padding:5px 9px}
|
||||
.mem-more{font-size:11px;color:var(--dim);text-align:center;padding:6px 0 2px}
|
||||
.ask-overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:60;display:flex;align-items:center;justify-content:center;padding:20px}
|
||||
.ask-box{background:var(--bg);border:1px solid var(--border);border-radius:10px;width:100%;max-width:420px;padding:20px;box-shadow:0 12px 40px rgba(0,0,0,.18);animation:askpop .12s ease-out}
|
||||
@keyframes askpop{from{opacity:0;transform:translateY(6px) scale(.98)}to{opacity:1;transform:none}}
|
||||
.ask-box{background:var(--bg);border:1px solid var(--border);border-radius:10px;width:100%;max-width:420px;padding:20px;box-shadow:0 12px 40px rgba(0,0,0,.18)}
|
||||
.ask-title{font-size:15px;font-weight:600;color:var(--text);margin-bottom:8px}
|
||||
.ask-msg{font-size:13px;color:var(--dim);line-height:1.5;white-space:pre-wrap;word-break:break-word}
|
||||
.ask-input{width:100%;box-sizing:border-box;margin-top:12px;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:9px 11px;font:inherit;font-size:13px}
|
||||
@@ -147,17 +149,15 @@ h1{margin:0;font-size:18px;color:var(--accent)}
|
||||
pastille dans les réglages. */
|
||||
#brand.agent #brand-a{color:var(--accent)}
|
||||
/* interrupteur (switch) */
|
||||
/* Sous-réglage du mode agent inaccessible quand l'agent est off : grisé + inerte. */
|
||||
/* Sous-réglages du mode agent présentés en CARTES (bordure + fond) pour ne pas
|
||||
les confondre avec les traits des sections principales. */
|
||||
/* Bloc du mode agent = carte de liste : le rembourrage est porté par les
|
||||
lignes (comme .slist), pas par la carte, sinon les retraits se cumulent. */
|
||||
/* ⚠️ PAS d'overflow:hidden ici : les bulles d'aide (« ? ») sont positionnées en
|
||||
absolu par rapport à la ligne, donc un débordement caché les TRONQUE — c'est
|
||||
ce qui rendait plusieurs infobulles illisibles. */
|
||||
.gated-block{margin:6px 0;border:1px solid var(--border);border-radius:10px;padding:0 0 10px;background:var(--panel);overflow:visible;transition:opacity .15s}
|
||||
.gated-block>.switchrow:first-of-type{padding-top:0}
|
||||
.gated-block.gated{opacity:.4;pointer-events:none;filter:grayscale(.4)}
|
||||
/* Sous-groupe du mode agent (Mémoire, Accès internet, Serveurs MCP, Paramètres).
|
||||
MÊME grammaire que « Machine » ou « Apparence » : étiquette de groupe NUE
|
||||
au-dessus, puis UNE carte .slist qui porte toutes les lignes. Auparavant
|
||||
chaque sous-groupe était une carte qui embarquait son propre titre et du
|
||||
contenu libre aux marges variables — quatre îlots décollés au lieu d'une
|
||||
colonne régulière. Le grisé « agent désactivé » se pose sur le GROUPE
|
||||
(étiquette comprise), plus sur la carte seule. */
|
||||
.gated-group{margin:0 0 10px;transition:opacity .15s}
|
||||
.gated-group.gated{opacity:.4;pointer-events:none;filter:grayscale(.4)}
|
||||
.switch{position:relative;display:inline-block;width:36px;height:20px;flex:none}
|
||||
.switch input{position:absolute;opacity:0;width:0;height:0}
|
||||
/* Piste décochée : un gris franc (pas --border), sinon le pavé blanc ne se
|
||||
@@ -184,10 +184,27 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
.preset.active{border-color:var(--accent);color:var(--accent);border-width:1.5px;padding:5.5px 9.5px}
|
||||
.preset>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
||||
.preset>button{flex-shrink:0}
|
||||
/* Liste de lignes cliquables (presets, pages mémoire) posée à fleur de carte :
|
||||
une pile de petites cartes bordées faisait « trop de carrés » là où le reste
|
||||
du menu aligne des lignes séparées par un filet. L'actif se signale par un
|
||||
voile et un liseré à gauche, plus par un cadre propre. */
|
||||
.rowlist>.preset{border:0;border-radius:0;margin:0;padding:6px 10px;background:transparent}
|
||||
.rowlist>.preset:not(:last-child){border-bottom:1px solid var(--border)}
|
||||
.rowlist>.preset:hover{background:var(--accent-bg)}
|
||||
.rowlist>.preset.active{background:var(--accent-bg);color:var(--text);font-weight:600;
|
||||
box-shadow:inset 2px 0 0 var(--text)}
|
||||
/* Ligne à deux étages (titre + étiquettes) : l'interligne interne suffit, on
|
||||
resserre donc le haut et le bas. */
|
||||
.rowlist>.preset .preset-meta{margin-top:4px}
|
||||
/* Messages et « voir plus » de la liste : alignés sur le retrait des lignes. */
|
||||
.rowlist>.muted,.rowlist>.mem-more{padding:9px 10px}
|
||||
.preset-info{flex:1;min-width:0}
|
||||
.preset-name{min-width:0;overflow-wrap:anywhere;word-break:break-word}
|
||||
.preset-edit{flex-shrink:0;align-self:flex-start;margin:-3px -4px 0 0;padding:2px 4px;font-size:13px;line-height:1;background:transparent;border:none;opacity:.6}
|
||||
.preset-edit:hover{opacity:1;color:var(--accent)}
|
||||
/* Icône d'engrenage : centrée verticalement sur la ligne (sa silhouette ronde se
|
||||
lit bien au milieu de son coin, là où le crayon penché tirait de travers). */
|
||||
.preset-edit{flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;
|
||||
margin:0 -2px 0 0;padding:4px;line-height:0;background:transparent;border:none;opacity:.55}
|
||||
.preset-edit:hover{opacity:1;color:var(--accent);background:transparent}
|
||||
.preset-meta{display:flex;align-items:center;gap:6px;margin-top:3px;flex-wrap:wrap}
|
||||
.qtag{flex-shrink:0;font-size:9px;padding:1px 6px;border:1px solid var(--border);color:var(--dim);border-radius:8px;letter-spacing:.03em;text-transform:uppercase}
|
||||
.rtag{flex-shrink:0;display:inline-flex;align-items:center;gap:3px;font-size:9px;padding:1px 6px;border:1px solid var(--border);color:var(--dim);border-radius:8px;letter-spacing:.03em}
|
||||
@@ -302,6 +319,21 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
align-items:center;justify-content:center;gap:12px;background:var(--bg);
|
||||
color:var(--dim);font-size:12px;letter-spacing:.04em;pointer-events:none}
|
||||
#chat-loading.show{display:flex}
|
||||
/* Accueil du fil vide. Posé en surimpression du chat (et non DANS le fil) pour ne
|
||||
pas perturber le défilement ni le rendu du replay ; pointer-events:none pour
|
||||
qu'il ne vole jamais un clic à la zone de saisie. Il vit sous le voile de
|
||||
chargement (z-index plus bas) : jamais les deux visibles en même temps. */
|
||||
#chat-empty{position:absolute;inset:0 0 150px;z-index:3;display:none;flex-direction:column;
|
||||
align-items:center;justify-content:center;gap:10px;pointer-events:none;
|
||||
opacity:0;transition:opacity .35s ease}
|
||||
#chat-empty.show{display:flex;opacity:1}
|
||||
/* Le clone perd les règles d'id de #brand (les id sont retirés), dont l'espace
|
||||
entre le « A » et le mot : on le repose ici, à l'échelle du logo agrandi
|
||||
(2 px pour 10 px de haut → 4 px pour 20 px), sinon le A s'encastre dans le J. */
|
||||
#ce-logo{display:inline-flex;align-items:center;gap:4px;color:var(--text)}
|
||||
#ce-logo svg{height:20px;width:auto;display:block;shape-rendering:crispEdges;fill:currentColor}
|
||||
#ce-logo .ce-word{display:inline-flex;color:var(--accent)}
|
||||
#ce-title{font-size:14px;color:var(--dim);letter-spacing:.03em}
|
||||
.cl-dots{display:inline-flex;gap:5px;align-items:center}
|
||||
.cl-dots span{width:7px;height:7px;border-radius:50%;background:var(--dim);animation:typingdot 1.2s infinite ease-in-out}
|
||||
.cl-dots span:nth-child(2){animation-delay:.16s}
|
||||
@@ -316,6 +348,15 @@ button:disabled{opacity:.5;cursor:not-allowed}
|
||||
défiler. Un seul gabarit, repris des listes de la modale de preset.
|
||||
Hauteurs : en-tête de section 34 px, ligne de réglage 34 px. */
|
||||
.side{padding:10px 12px}
|
||||
/* ⚠️ Le raccourci `padding` ci-dessus écrase les esquives d'encoche posées plus
|
||||
haut (règles tiroir). Quand la barre est un TIROIR (téléphone, ou mode
|
||||
« barre en tiroir » sur ordinateur), elle est en position:fixed : le padding
|
||||
du <body> ne la protège pas, il faut donc reposer les env() ici — sinon
|
||||
« AJEAN » et la pastille d'état passent sous l'encoche en PWA iOS. */
|
||||
@media (max-width:720px){
|
||||
.side{padding:calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left))}
|
||||
}
|
||||
[data-hide-side="1"] .side{padding:calc(10px + env(safe-area-inset-top)) 12px calc(10px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left))}
|
||||
.sidehead{margin-bottom:10px}
|
||||
|
||||
/* --- En-tête de section --- */
|
||||
@@ -362,25 +403,40 @@ details[open]>summary::after{transform:rotate(45deg)}
|
||||
.subhead,.io-cap,.pages-bar .pages-title,.skillshead>span{position:relative;display:flex;align-items:center;
|
||||
font-size:11px;font-weight:600;color:var(--dim);margin:10px 2px 5px}
|
||||
.io-cap{margin:0 2px 5px}
|
||||
/* Premier élément d'une carte : sa marge haute s'ajoutait au rembourrage de la
|
||||
carte (18 px de vide au-dessus de « Mémoire », « Accès internet », etc.). */
|
||||
.gated-block>:first-child{margin-top:0}
|
||||
/* Étiquette de groupe en tête d'un sous-groupe du mode agent : elle suit une
|
||||
carte, elle a donc besoin de son air normal — mais pas d'un cumul de marges. */
|
||||
.gated-group>.subhead:first-child{margin-top:12px}
|
||||
/* Une étiquette de groupe qui suit immédiatement le titre de section n'a pas
|
||||
besoin de reprendre de l'air : le titre en a déjà donné. */
|
||||
details>summary+.subhead{margin-top:4px}
|
||||
.io-group{margin-top:8px}
|
||||
|
||||
/* --- Carte de liste --- */
|
||||
/* overflow VISIBLE : cf. la note sur .gated-block — les infobulles doivent
|
||||
/* overflow VISIBLE : les infobulles (« ? ») sont positionnées en absolu par
|
||||
rapport à la ligne, un débordement caché les TRONQUERAIT — elles doivent
|
||||
pouvoir déborder de la carte. Les lignes n'ont pas de fond propre, l'arrondi
|
||||
n'a donc rien à rogner. */
|
||||
.slist,.gated-block{background:var(--panel);border:1px solid var(--border);border-radius:9px;
|
||||
.slist{background:var(--panel);border:1px solid var(--border);border-radius:9px;
|
||||
overflow:visible;margin:0 0 8px}
|
||||
.slist:last-child,.gated-block:last-child{margin-bottom:0}
|
||||
.slist:last-child{margin-bottom:0}
|
||||
/* Contenu libre (champ + boutons) : le rembourrage passe sur la carte. */
|
||||
.slist.pad,.gated-block{padding:8px 10px}
|
||||
.slist.pad{padding:8px 10px}
|
||||
.slist.pad>:first-child{margin-top:0}
|
||||
.slist.pad>:last-child{margin-bottom:0}
|
||||
/* Contenu libre posé DANS une carte de lignes (champs Crawl4AI, liste MCP,
|
||||
pages mémoire) : même rembourrage et même filet de séparation qu'une ligne,
|
||||
pour qu'il ne flotte pas au milieu de la carte. */
|
||||
.slist>.rowbody{padding:9px 10px}
|
||||
.slist>.rowbody:not(:first-child),.slist>.pages-bar:not(:first-child){border-top:1px solid var(--border)}
|
||||
/* …sauf juste après une ligne de réglage : elle porte DÉJÀ son filet bas, les
|
||||
deux se cumuleraient en un trait double. */
|
||||
.slist>.switchrow+.rowbody,.slist>.switchrow+.pages-bar{border-top:0}
|
||||
.slist>.rowbody>:first-child{margin-top:0}
|
||||
.slist>.rowbody>:last-child{margin-bottom:0}
|
||||
.slist>.pages-bar{min-height:34px;padding:4px 10px;margin-top:0}
|
||||
/* Contrôle posé à droite d'une ligne (liste « mode » de la mémoire) : même place
|
||||
que l'interrupteur, largeur ajustée au contenu. */
|
||||
.switchrow>.sctl{margin-left:auto;order:2;width:auto;max-width:62%}
|
||||
|
||||
/* --- Ligne de réglage : hauteur constante, contrôle à droite --- */
|
||||
.switchrow{position:relative;display:flex;align-items:center;gap:10px;min-height:34px;padding:6px 0;
|
||||
@@ -412,9 +468,17 @@ details>summary+.subhead{margin-top:4px}
|
||||
border-radius:7px;padding:7px 9px;font:inherit;font-size:12.5px}
|
||||
.sctl:focus,.io-input:focus{outline:none;border-color:var(--dim)}
|
||||
textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
.side button:not(.preset-edit):not(.addbtn):not(.preset-add):not(.copybtn){font-size:12px;padding:6px 10px;border-radius:7px}
|
||||
.side button:not(.preset-edit):not(.addbtn):not(.preset-add):not(.copybtn):not(.iconbtn){font-size:12px;padding:6px 10px;border-radius:7px}
|
||||
.side .row{display:flex;gap:6px;align-items:center;margin:6px 0}
|
||||
.side .row .sctl,.side .row .io-input{flex:1;min-width:0}
|
||||
/* Bouton d'action à côté d'un champ (copier, ouvrir) : un CARRÉ à la hauteur du
|
||||
champ, icône au trait centrée. Le glyphe seul (⧉, ↗) dans un bouton texte se
|
||||
posait de travers et changeait de largeur d'une police à l'autre. */
|
||||
.iconbtn{flex:none;display:inline-flex;align-items:center;justify-content:center;
|
||||
width:34px;height:34px;padding:0;margin:0;color:var(--dim);background:var(--panel);
|
||||
border:1px solid var(--border);border-radius:9px;cursor:pointer}
|
||||
.iconbtn:hover{color:var(--text);border-color:var(--dim);background:var(--accent-bg)}
|
||||
.iconbtn svg{width:15px;height:15px;display:block}
|
||||
.side .btn-danger-o{border-color:var(--err);color:var(--err)}
|
||||
.side .btn-danger-o:hover{background:var(--danger-bg);border-color:var(--err);color:var(--err)}
|
||||
.side .muted{font-size:11.5px;line-height:1.5}
|
||||
@@ -512,11 +576,16 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
#lc-chip{height:32px;font-size:11px;left:calc(46px + env(safe-area-inset-left));top:calc(6px + env(safe-area-inset-top))}
|
||||
}
|
||||
/* --- Backend llama.cpp (2 modes : rapide / optimisée) --------------------- */
|
||||
.lc-head{font-size:12px;line-height:1.5;margin-bottom:10px}
|
||||
.lc-modes{display:flex;flex-direction:column;gap:8px}
|
||||
.lc-head{font-size:12px;line-height:1.5}
|
||||
/* Les trois modes sont des LIGNES de la carte d'installation (elles étaient des
|
||||
cartes dans une carte) : pas de cadre propre, un filet entre elles, comme
|
||||
n'importe quelle ligne de réglage du menu. */
|
||||
.lc-modes{display:flex;flex-direction:column}
|
||||
.lc-modes.busy{opacity:.5;pointer-events:none}
|
||||
.lc-mode{border:1px solid var(--border);border-radius:8px;padding:10px 12px;cursor:pointer;transition:border-color .12s,background .12s}
|
||||
.lc-mode:hover{border-color:var(--dim)}
|
||||
.slist>.lc-modes:not(:first-child){border-top:1px solid var(--border)}
|
||||
.lc-mode{padding:10px;cursor:pointer;transition:background .12s}
|
||||
.lc-mode:not(:last-child){border-bottom:1px solid var(--border)}
|
||||
.lc-mode:hover{background:var(--accent-bg)}
|
||||
.lc-mode-t{font-size:13px;font-weight:600;color:var(--text);display:flex;align-items:center;gap:6px}
|
||||
/* « conseillé » : pilule PLEINE (texte inversé). En contour, elle héritait du
|
||||
gris atténué du thème monochrome et devenait illisible en mode sombre. */
|
||||
@@ -664,19 +733,51 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
.pe-adv.pe-raw-plain>summary{display:none}
|
||||
.pe-adv.pe-raw-plain .pe-adv-body{padding:0}
|
||||
/* Pied de modale. */
|
||||
.pe-foot{padding:13px 16px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.pe-foot .foot-r{display:flex;gap:8px}
|
||||
.pe-btn{margin:0;padding:9px 18px;border-radius:9px;font-weight:600;cursor:pointer;border:1px solid var(--border);background:var(--panel);color:var(--text)}
|
||||
.pe-foot{padding:9px 12px;border-top:1px solid var(--border);display:flex;justify-content:space-between;
|
||||
align-items:center;gap:8px;flex-wrap:nowrap}
|
||||
.pe-foot .foot-l{display:flex;align-items:center;gap:8px;min-width:0}
|
||||
.pe-foot .foot-r{display:flex;gap:8px;flex:none}
|
||||
.pe-btn{margin:0;padding:7px 15px;font-size:12.5px;border-radius:8px;font-weight:600;cursor:pointer;
|
||||
border:1px solid var(--border);background:var(--panel);color:var(--text);white-space:nowrap}
|
||||
.pe-btn:hover{border-color:var(--dim)}
|
||||
.pe-btn.primary{border-color:var(--accent);background:var(--accent);color:var(--bg)}
|
||||
.pe-btn.primary:hover{filter:brightness(1.08)}
|
||||
.pe-btn.ghost-danger{border-color:transparent;background:transparent;color:var(--err);padding:9px 10px}
|
||||
.pe-btn.ghost-danger:hover{background:var(--danger-bg)}
|
||||
.pe-delmodel{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--dim);cursor:pointer}
|
||||
.pe-delmodel input{margin:0}
|
||||
/* Suppression : icône seule, en retrait. Un bouton plein « supprimer » pesait
|
||||
autant que « enregistrer » alors que c'est l'action rare — et à trois boutons
|
||||
larges, le pied passait à deux lignes sur téléphone. */
|
||||
.pe-del{flex:none;display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;
|
||||
margin:0;padding:0;border:1px solid transparent;border-radius:8px;background:transparent;color:var(--err);cursor:pointer}
|
||||
.pe-del svg{width:15px;height:15px;display:block}
|
||||
.pe-del:hover{background:var(--danger-bg);border-color:color-mix(in srgb,var(--err) 40%,transparent)}
|
||||
/* Option d'une confirmation : une PASTILLE à cocher, pas une case système suivie
|
||||
d'un bout de texte. Éteinte : contour discret. Allumée : elle passe au rouge,
|
||||
comme l'action destructrice qu'elle étend. Vit dans la boîte de confirmation
|
||||
(#ask-check), plus dans le pied de l'éditeur. */
|
||||
.pe-delmodel{align-self:flex-start;margin-top:14px;display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:600;
|
||||
letter-spacing:.02em;color:var(--dim);cursor:pointer;white-space:nowrap;
|
||||
padding:5px 11px;border:1px solid var(--border);border-radius:999px;background:var(--panel);
|
||||
transition:color .12s,border-color .12s,background .12s}
|
||||
.pe-delmodel input{position:absolute;opacity:0;width:0;height:0}
|
||||
/* Le point tient lieu de case à cocher : creux au repos, plein une fois coché. */
|
||||
.pe-delmodel::before{content:"";width:6px;height:6px;border-radius:50%;flex:none;
|
||||
border:1px solid currentColor;background:transparent}
|
||||
.pe-delmodel:hover{color:var(--text);border-color:var(--dim)}
|
||||
.pe-delmodel:has(input:checked){color:var(--err);border-color:color-mix(in srgb,var(--err) 45%,transparent);background:var(--danger-bg)}
|
||||
.pe-delmodel:has(input:checked)::before{background:currentColor}
|
||||
/* Éditeur de preset/mémoire — mise en forme propre du modale */
|
||||
.modal-ov{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:30;align-items:center;justify-content:center;padding:20px}
|
||||
.modal-card{background:var(--bg);border:1px solid var(--border);border-radius:12px;width:100%;max-width:600px;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 16px 50px rgba(0,0,0,.18)}
|
||||
/* Ouverture/fermeture animées. L'état par défaut est l'état FERMÉ ; la classe
|
||||
.show (posée par showModal/hideModal) fait jouer la transition dans les deux
|
||||
sens — une @keyframes n'aurait animé que l'ouverture. */
|
||||
.modal-ov,.ask-overlay{opacity:0;transition:opacity .16s ease}
|
||||
.modal-ov.show,.ask-overlay.show{opacity:1}
|
||||
.modal-card,.ask-box{transform:translateY(10px) scale(.985);opacity:0;
|
||||
transition:transform .18s cubic-bezier(.2,.8,.3,1),opacity .18s ease}
|
||||
.modal-ov.show .modal-card,.ask-overlay.show .ask-box{transform:none;opacity:1}
|
||||
@media (prefers-reduced-motion:reduce){
|
||||
.modal-ov,.ask-overlay,.modal-card,.ask-box{transition:none}
|
||||
}
|
||||
.modal-card{position:relative;background:var(--bg);border:1px solid var(--border);border-radius:12px;width:100%;max-width:600px;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 16px 50px rgba(0,0,0,.18)}
|
||||
.modal-head{padding:14px 16px;border-bottom:1px solid var(--border);display:flex;justify-content:space-between;align-items:center}
|
||||
.modal-head strong{font-size:14px}
|
||||
/* Croix de fermeture : un CARRÉ, pas un rectangle. Avec un padding asymétrique
|
||||
@@ -684,6 +785,25 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
clic était basse. 32px, glyphe centré, taille de cible correcte au doigt. */
|
||||
.modal-x{margin:0;padding:0;width:32px;height:32px;flex:none;display:inline-flex;align-items:center;justify-content:center;font-size:19px;line-height:1;color:var(--dim)}
|
||||
.modal-body{padding:16px;overflow:auto;flex:1}
|
||||
/* Modale ouverte mais pas encore remplie : on affiche le voile de chargement À LA
|
||||
PLACE du formulaire et du pied. Estomper le formulaire ne suffisait pas — on
|
||||
voyait quand même les champs s'écrire un par un derrière le voile. */
|
||||
/* Fond TRANSPARENT : le formulaire est déjà rendu invisible par `visibility`,
|
||||
un fond opaque ne servirait qu'à masquer aussi le titre de la modale. */
|
||||
.modal-load{display:none;position:absolute;inset:0;z-index:2;align-items:center;justify-content:center;
|
||||
pointer-events:none}
|
||||
.modal-ov.loading .modal-load{display:flex}
|
||||
/* visibility (et non display) : le formulaire garde sa place, donc la carte a
|
||||
déjà sa taille finale — la révéler ne provoque plus d'à-coup de hauteur. */
|
||||
.modal-ov.loading .modal-body,
|
||||
.modal-ov.loading .pe-body,
|
||||
.modal-ov.loading .pe-foot,
|
||||
.modal-ov.loading .modal-foot{visibility:hidden}
|
||||
/* Rond qui tourne — les trois points sont la signature du CHAT, pas des modales. */
|
||||
.spinner{width:22px;height:22px;border-radius:50%;border:2px solid var(--border);
|
||||
border-top-color:var(--dim);animation:spin .7s linear infinite}
|
||||
@keyframes spin{to{transform:rotate(360deg)}}
|
||||
@media (prefers-reduced-motion:reduce){.spinner{animation-duration:2s}}
|
||||
.modal-foot{padding:12px 16px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}
|
||||
.foot-l{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
||||
.foot-r{display:flex;gap:8px}
|
||||
@@ -714,7 +834,7 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
.btn-save{border-color:var(--accent);color:var(--accent);margin:0}
|
||||
.del-model{align-items:center;gap:5px;font-size:11px;color:var(--dim)}
|
||||
.del-model input{margin:0}
|
||||
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:12px;color:var(--dim);font-weight:600;padding:6px 0;margin-top:4px}
|
||||
.lc-logbar{display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none;font-size:12px;color:var(--dim);font-weight:600;padding:6px 0;margin-top:2px}
|
||||
.lc-logbar #lc-log-caret{font-size:9px;transition:transform .12s;display:inline-block}
|
||||
.lc-logbar.open #lc-log-caret{transform:rotate(90deg)}
|
||||
#lc-log{background:var(--panel);border:1px solid var(--border);border-radius:6px;padding:8px;font-size:10px;line-height:1.45;max-height:220px;overflow:auto;white-space:pre-wrap;word-break:break-all;margin:0}
|
||||
@@ -748,11 +868,6 @@ textarea.sctl{min-height:76px;resize:vertical;line-height:1.5}
|
||||
compacts, pour une présentation homogène et sans surcharge visuelle. */
|
||||
.io-input{flex:1;min-width:0;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:9px;padding:8px 10px;font:inherit;font-size:13px}
|
||||
|
||||
/* Toggles de la sous-section « Paramètres » du mode agent : chacun dans sa
|
||||
propre carte bordée et arrondie (comme les serveurs MCP), plus lisible. */
|
||||
#param-block .switchrow{border:1px solid var(--border);border-radius:8px;padding:9px 11px;background:var(--bg)}
|
||||
#param-block .switchrow+.switchrow{margin-top:8px}
|
||||
|
||||
/* Modale MCP (ajout/édition) : les champs des groupes stdio/http n'avaient aucun
|
||||
espacement vertical (le conteneur n'est pas un flex à gap) → ils se touchaient.
|
||||
On aligne leur rythme sur celui du reste du formulaire (.mform). */
|
||||
@@ -840,13 +955,13 @@ html[data-hide-stats="1"] #chat .msg .statline{display:none}
|
||||
#ctx-fill{background:var(--text);opacity:.75}
|
||||
#sendhint{order:4;padding:12px var(--col) 2px}
|
||||
/* Reste de l'UI : coins arrondis et contrastes doux, dans le même esprit. */
|
||||
button,.addbtn,.mem-search{border-radius:10px}
|
||||
button,.mem-search{border-radius:10px}
|
||||
/* Champ + bouton accolés : on ne garde l'arrondi que sur les coins extérieurs. */
|
||||
.pe-inpbtn .pe-inp{border-radius:10px 0 0 10px}
|
||||
.pe-inpbtn button{border-radius:0 10px 10px 0}
|
||||
.input-btn .ctl{border-radius:10px 0 0 10px}
|
||||
.input-btn .btn-in{border-radius:0 10px 10px 0}
|
||||
.preset,.gated-block,.lc-mode{border-radius:12px}
|
||||
.preset{border-radius:12px}
|
||||
/* Les presets restent des cartes (fond + bordure) : à plat, sans fond ni
|
||||
cadre, la liste ne se lisait plus comme des lignes distinctes. L'actif se
|
||||
signale par sa bordure — un simple voile d'accent (7 % de blanc sur fond
|
||||
|
||||
Reference in new issue
Block a user