mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-17 20:26:35 +02:00
Un seul jeu d'icônes pour toute l'interface
Les deux icônes de la carte de saisie venaient de la maquette ; tout le reste était encore des GLYPHES DE TEXTE — ↻ × ☰ ⌫ 🗑 ✎ ↓ ▸ ▣ ·. C'est le système d'exploitation qui les dessinait : le même bouton sortait fin sur macOS, gras et bleuté sur Windows (émojis en couleur), et absent d'une machine sans police d'émojis. Aucune cohérence possible, et rien à régler depuis la feuille de style. Elles passent en SVG, tracées comme celles de la maquette : boîte de 24, trait de 1,8, extrémités arrondies, couleur héritée. Un seul fichier les décrit (03-icons.js) — menu, fermer, rafraîchir, crayon, corbeille, téléchargement, flèche, dossier, fichier, image, plus, chevron. Touchées : le ☰ du téléphone, les croix de modale, les deux rafraîchissements, le « + » des sections, la flèche de retour en bas, le téléchargement Hugging Face, « vider la discussion », les lignes du panneau Fichiers (dossier, image, fichier, télécharger, supprimer), renommer/supprimer une discussion, éditer un serveur MCP. Deux pièges rencontrés, notés dans la feuille : - Une règle de centrage qui pose `display:inline-flex` sur ces boutons les rend VISIBLES en permanence : plusieurs se masquent justement par `display:none` (le ☰ hors téléphone, le « + » d'une section fermée, la flèche de retour en bas). Le ☰ s'est ainsi retrouvé par-dessus l'en-tête sur grand écran. Le centrage passe donc par le rembourrage pour ceux-là. - Les titres de panneau (.stitle) et l'intertitre « Paramètres » gardaient le monospace en capitales de la charte précédente ; ils suivent maintenant le seul style de titre de la maquette, celui de « Performance ». Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
This commit is contained in:
7 files changed
+236
-50
No files matched your search
@@ -0,0 +1,51 @@
|
||||
// ─── Jeu d'icônes ────────────────────────────────────────────────────────────
|
||||
// Un seul trait pour toute l'interface : 24 unités de côté, 1,8 d'épaisseur,
|
||||
// extrémités et jonctions arrondies — celui des deux icônes de la maquette
|
||||
// (dépôt et envoi), étendu au reste de l'application.
|
||||
//
|
||||
// Elles étaient jusqu'ici des GLYPHES DE TEXTE (↻ × ☰ 🗑 ✎ ↓ ▸). C'est le
|
||||
// système d'exploitation qui les dessinait : le même bouton sortait fin sur
|
||||
// macOS, gras et bleuté sur Windows (émojis en couleur), et carrément absent sur
|
||||
// un serveur sans police d'émojis. Aucune cohérence possible, et rien à régler
|
||||
// depuis la feuille de style. En SVG, elles suivent currentColor et la taille
|
||||
// qu'on leur donne.
|
||||
//
|
||||
// Ajouter une icône : une entrée ici, et rien d'autre — surtout pas un second
|
||||
// dessin ailleurs, c'est ainsi que le trait finit par diverger.
|
||||
const ICONS = {
|
||||
menu: '<path d="M4 7h16"/><path d="M4 12h16"/><path d="M4 17h16"/>',
|
||||
close: '<path d="M6.5 6.5l11 11"/><path d="M17.5 6.5l-11 11"/>',
|
||||
refresh: '<path d="M20.5 12a8.5 8.5 0 1 1-2.5-6"/><path d="M20.5 3.5V9H15"/>',
|
||||
pencil: '<path d="M4 20h4L19 9a2.1 2.1 0 0 0-3-3L5 17z"/><path d="M14.5 7.5l3 3"/>',
|
||||
trash: '<path d="M4 7h16"/><path d="M9.5 7V4.5h5V7"/><path d="M6.5 7l1 12.5h9l1-12.5"/>',
|
||||
download: '<path d="M12 4v11"/><path d="M7.5 10.5 12 15l4.5-4.5"/><path d="M4.5 19.5h15"/>',
|
||||
arrowDown: '<path d="M12 5v14"/><path d="M6 13l6 6 6-6"/>',
|
||||
folder: '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>',
|
||||
file: '<path d="M13.5 3.5H7A1.5 1.5 0 0 0 5.5 5v14A1.5 1.5 0 0 0 7 20.5h10a1.5 1.5 0 0 0 1.5-1.5V8.5z"/><path d="M13.5 3.5V8.5h5"/>',
|
||||
image: '<rect x="3.5" y="5" width="17" height="14" rx="2"/><circle cx="8.5" cy="10" r="1.4"/><path d="M4.5 17.5 10 12l3.5 3.5L16 13l3.5 3.5"/>',
|
||||
plus: '<path d="M12 5.5v13"/><path d="M5.5 12h13"/>',
|
||||
chevron: '<path d="M9.5 5.5 16 12l-6.5 6.5"/>',
|
||||
search: '<circle cx="11" cy="11" r="6.5"/><path d="M20 20l-4.4-4.4"/>',
|
||||
sun: '<circle cx="12" cy="12" r="4.2"/><path d="M12 2.5v2.2"/><path d="M12 19.3v2.2"/><path d="M4.2 4.2l1.6 1.6"/><path d="M18.2 18.2l1.6 1.6"/><path d="M2.5 12h2.2"/><path d="M19.3 12h2.2"/><path d="M4.2 19.8l1.6-1.6"/><path d="M18.2 5.8l1.6-1.6"/>',
|
||||
};
|
||||
|
||||
// iconHTML rend l'icône en chaîne, pour les endroits qui composent déjà du
|
||||
// balisage. `aria-hidden` : le sens est porté par le title/aria-label du bouton,
|
||||
// jamais par le dessin.
|
||||
function iconHTML(name, size){
|
||||
const d = ICONS[name];
|
||||
if(!d) return '';
|
||||
const n = size || 18;
|
||||
return '<svg viewBox="0 0 24 24" width="' + n + '" height="' + n + '" fill="none" '
|
||||
+ 'stroke="currentColor" stroke-width="1.8" stroke-linecap="round" '
|
||||
+ 'stroke-linejoin="round" aria-hidden="true">' + d + '</svg>';
|
||||
}
|
||||
|
||||
// icon rend l'icône en ÉLÉMENT, pour les listes construites en DOM (fichiers,
|
||||
// discussions) où l'on ne veut pas d'innerHTML.
|
||||
function icon(name, size){
|
||||
const span = document.createElement('span');
|
||||
span.className = 'icn';
|
||||
span.innerHTML = iconHTML(name, size); // contenu constant, jamais de donnée
|
||||
return span.firstElementChild || span;
|
||||
}
|
||||
@@ -63,7 +63,8 @@ function renderMCP(r){
|
||||
sw.appendChild(cb); sw.appendChild(sl);
|
||||
|
||||
const edit = document.createElement('button');
|
||||
edit.className = 'mcp-edit'; edit.title = 'éditer'; edit.textContent = '✎';
|
||||
edit.className = 'mcp-edit'; edit.title = 'éditer';
|
||||
edit.setAttribute('aria-label', 'éditer ce serveur'); edit.appendChild(icon('pencil', 15));
|
||||
edit.onclick = (e)=>{ e.stopPropagation(); openMcp(s.name); };
|
||||
|
||||
row.appendChild(dot); row.appendChild(info); row.appendChild(sw); row.appendChild(edit);
|
||||
|
||||
@@ -142,7 +142,7 @@ function fileRow(e){
|
||||
|
||||
const ic = document.createElement('span');
|
||||
ic.className = 'files-ic';
|
||||
ic.textContent = e.dir ? '▸' : (e.image ? '▣' : '·');
|
||||
ic.appendChild(icon(e.dir ? 'folder' : (e.image ? 'image' : 'file'), 16));
|
||||
row.appendChild(ic);
|
||||
|
||||
const mid = document.createElement('span');
|
||||
@@ -171,12 +171,14 @@ function fileRow(e){
|
||||
acts.className = 'files-acts';
|
||||
if(!e.dir){
|
||||
const dl = document.createElement('button');
|
||||
dl.className = 'iconbtn'; dl.textContent = '↓'; dl.title = 'télécharger';
|
||||
dl.className = 'iconbtn'; dl.appendChild(icon('download', 16));
|
||||
dl.title = 'télécharger'; dl.setAttribute('aria-label', 'télécharger');
|
||||
dl.onclick = ev=>{ ev.stopPropagation(); downloadWorkspaceFile(e.path, e.name, row); };
|
||||
acts.appendChild(dl);
|
||||
}
|
||||
const rm = document.createElement('button');
|
||||
rm.className = 'iconbtn'; rm.textContent = '🗑'; rm.title = 'supprimer';
|
||||
rm.className = 'iconbtn'; rm.appendChild(icon('trash', 16));
|
||||
rm.title = 'supprimer'; rm.setAttribute('aria-label', 'supprimer');
|
||||
rm.onclick = ev=>{ ev.stopPropagation(); removeFile(e); };
|
||||
acts.appendChild(rm);
|
||||
row.appendChild(acts);
|
||||
|
||||
@@ -60,10 +60,14 @@ function renderConversations(){
|
||||
|
||||
// Renommer / supprimer : stopPropagation, sinon le clic bascule aussi.
|
||||
const acts = document.createElement('div'); acts.className = 'conv-acts';
|
||||
const ren = document.createElement('button'); ren.className = 'iconbtn'; ren.textContent = '✎';
|
||||
ren.title = 'renommer'; ren.onclick = e => { e.stopPropagation(); convRename(c.id, title); };
|
||||
const del = document.createElement('button'); del.className = 'iconbtn'; del.textContent = '🗑';
|
||||
del.title = 'supprimer'; del.onclick = e => { e.stopPropagation(); convDelete(c.id, title); };
|
||||
const ren = document.createElement('button'); ren.className = 'iconbtn';
|
||||
ren.appendChild(icon('pencil', 15)); ren.title = 'renommer';
|
||||
ren.setAttribute('aria-label', 'renommer la discussion');
|
||||
ren.onclick = e => { e.stopPropagation(); convRename(c.id, title); };
|
||||
const del = document.createElement('button'); del.className = 'iconbtn';
|
||||
del.appendChild(icon('trash', 15)); del.title = 'supprimer';
|
||||
del.setAttribute('aria-label', 'supprimer la discussion');
|
||||
del.onclick = e => { e.stopPropagation(); convDelete(c.id, title); };
|
||||
acts.appendChild(ren); acts.appendChild(del);
|
||||
|
||||
row.append(info, when, acts);
|
||||
|
||||
Reference in new issue
Block a user