mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-11 17:26:57 +02:00
a1047b4e08ad335a847a877b76b10603b217ddea
33
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a1047b4e08 |
Boutons : leur rendre leur cadre
La refonte les voulait « sans contour » : trait transparent et aplat pris sur --row-bg. Or --row-bg est exactement le fond des blocs qui les contiennent (.slist, .rowbody). Un bouton posé dessus n'avait donc ni trait ni écart de valeur : « démarrer / redémarrer / arrêter » et « mettre à jour le moteur » se lisaient comme du texte, et rien ne disait qu'on pouvait cliquer. On revient au cadre d'avant la refonte — trait de 1 px et aplat un ton au-dessus du bloc — pour que le bouton se détache quel que soit ce qu'il y a derrière. La micro-interaction au survol reste : elle était bonne, il lui manquait seulement quelque chose à animer, et le survol change maintenant aussi la couleur du trait. Les boutons-icônes de la saisie et de l'en-tête (envoyer, arrêter, joindre, ☰) ne bougent pas : ils posent border:0 plus bas dans la feuille et gardent leur allure nue, qui est la bonne pour un pictogramme seul. Vérifié en clair et en sombre, dans la barre latérale, les boîtes de dialogue et les modales. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
ee60ec8480 |
Réglages : rendre au congé la place de tourner
Les chevrons des sections débordaient dans l'arrondi de la carte, et le rayon paraissait beaucoup trop fort. La cause n'était pas le rayon : la carte des réglages avait perdu sa gouttière intérieure. Le gabarit de carte (commit précédent) a déplacé le retrait vers #side-scroll pour aligner Discussions, Réglages et Performance sur la même largeur — mais il a mis le rembourrage de .sidegroup à zéro au lieu de ne déplacer que le retrait EXTÉRIEUR. Les lignes allaient donc d'un bord à l'autre : sur la première et la dernière, le chevron tombait dans le congé de 20 px et semblait en sortir, et les filets de séparation venaient buter dans la courbe. On reprend le retrait d'avant la refonte — 16 px, exactement la valeur qui fonctionnait — et tout rentre dans l'ordre sans toucher au rayon. Vérifié en clair, en sombre et en largeur téléphone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
b378311873 |
Moteur : mettre à jour llama.cpp sans reconstruire l'image
llama.cpp publie plusieurs versions par jour ; l'image de Loki ne se reconstruit qu'à une mise à jour de Loki. Le moteur y était donc figé à la date du dernier build, et le rattraper imposait un rebuild complet de 2,6 Go pour un composant qui en pèse 170 Mo. Le panneau « Moteur » ne le disait même pas : il annonçait « rien à mettre à jour ici ». Réglages → Moteur affiche maintenant la version qui tourne (bXXXXX et son commit, lus dans la bannière du binaire — jusqu'ici invisibles ailleurs que dans le journal du moteur) et la met à jour en un clic. Où le moteur est pris. Pas dans les releases GitHub de llama.cpp : elles ne contiennent AUCUN binaire CUDA pour Linux, et le mode « précompilé » retomberait sur Vulkan, donc sur une régression pour une carte NVIDIA. La seule distribution CUDA/Linux officielle et précompilée est l'image de conteneur — celle-là même dont l'image de Loki hérite. On lit son manifeste OCI et on ne télécharge que les couches qui portent /app, en descendant du sommet : le runtime CUDA (2 Go) et la base système sont déjà là. S'arrêter au binaire ne suffit pas — llama.cpp le range dans une couche et ses .so dans la précédente — d'où une règle d'arrêt sur « binaire + libggml-base + libllama », et un garde-fou de taille qui interdit de descendre jusqu'au runtime. Le moteur atterrit dans /data/engine/<version>/, donc sur le volume de données : il survit à un docker compose pull. La variante (CUDA, Vulkan, SYCL, MUSA, CPU) est déduite des backends ggml posés à côté du moteur courant — le conteneur ne sait pas de quelle image il vient, et faire retenir « server-cuda » à l'utilisateur serait un piège. Le risque, et ce qui le couvre. La mise à jour apporte llama.cpp, pas le runtime CUDA, qui reste celui de l'image : un llama.cpp compilé pour un CUDA plus récent ne chargerait pas son backend GPU. Le symptôme serait silencieux — tout marche, mais sur le processeur. Le nouveau moteur est donc lancé à blanc avant toute bascule ; il est refusé s'il ne démarre pas, ET s'il ne voit plus aucune carte alors que le moteur courant en voyait. Dans les deux cas le moteur courant n'est pas touché, et celui de l'image reste intact : « revenir au moteur de l'image » y ramène en un clic, sans réseau. Vérifié de bout en bout contre le vrai ghcr.io (166 Mo, 7 s, toutes les bibliothèques et leurs liens de version présents) et, pour les chemins d'échec, contre un faux registre. LOKI_OCI_REGISTRY permet de viser un miroir quand ghcr.io n'est pas joignable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
e1d0e8f04a |
Discussions et moniteur : le gabarit de carte des réglages
La maquette posait ces deux blocs à plat, pleine largeur — et c'est ce que j'avais suivi. Dans l'application réelle, ils voisinent avec les sections de réglages, qui sont des CARTES : deux traitements pour deux listes qui se touchent, et la barre latérale se lisait comme deux morceaux collés. Ils reprennent donc le gabarit dominant de la colonne (.sidegroup) : fond de panneau, rayon de 20, ombre discrète, lignes séparées d'un filet et rognées par overflow plutôt qu'arrondies une à une. La gouttière latérale revient (le plein format n'a plus lieu d'être), le titre « Performance » devient une étiquette de groupe posée AU-DESSUS de sa carte — comme « Paramètres » au-dessus de la sienne — et la sélection reste un aplat plein, un cadre ferait un second contour à l'intérieur du premier. Corrigé au passage : la remise à zéro de marge sur la dernière jauge. Elle datait d'un temps où il n'y avait qu'un conteneur ; depuis que les cartes graphiques et la mémoire vive sont deux blocs distincts, la dernière jauge de CHAQUE bloc perdait sa marge et « Mémoire vive » venait se coller à la VRAM. C'est le rembourrage bas de la carte qui absorbe désormais la dernière marge. Vérifié dans les deux thèmes, avec deux cartes graphiques et trois discussions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
b518e98b43 |
Accès OpenAI : servi par Loki, par domaine ou par IP
L'endpoint compatible OpenAI n'était pas servi par Loki : le panneau annonçait l'adresse de llama-server lui-même, http://<ip>:8080/v1. Dans le déploiement de référence de ce fork, cette adresse ne peut joindre personne — le port 8080 n'est pas publié par le conteneur, l'entrypoint sème HOST=127.0.0.1, et l'IP annoncée est celle du bridge Docker. L'autre voie proposée, « exposer en public (ajean.link) », exigeait un jeton de relais que ce fork ne permet plus d'obtenir : l'interrupteur ne pouvait que renvoyer vers un panneau supprimé. Désormais, Loki sert /v1/* SUR SON PROPRE PORT et relaie vers le moteur. L'API est donc joignable partout où l'interface l'est — IP du réseau local, nom de domaine, reverse proxy — sans publier de second port ni ouvrir le moteur. Serveur - mountOAI (llm_oai.go) monte /v1/ sur le mux, et RIEN d'autre : ni /metrics, ni /props, ni /slots, qui divulgueraient le modèle chargé et l'état des slots. Le filtre interne d'oaiHandler reste en seconde barrière. - requireCompletionKey (web_auth.go) garde cette surface avec la clé des COMPLÉTIONS, pas celle de pilotage : un client OpenAI n'a qu'un en-tête Authorization, et on veut pouvoir lui donner l'accès au modèle sans le droit de redémarrer la machine. Erreurs au format d'OpenAI (body.error.message), que les SDK savent présenter. Le préflight CORS passe sans clé — il n'en porte jamais, et le refuser casserait tout client tiers de navigateur. - effectiveAPIKeyErr (backend_config.go) devient la source unique de la clé exigée : base d'abord, config.env en repli, exactement comme le moteur. Sans ce miroir, un API_KEY résiduel donnait un endpoint « ouvert » côté Loki et un 401 côté moteur, sans rien pour l'expliquer. Lecture ratée = refus, jamais ouverture (même raisonnement que readWebKeyErr). - oaiHandler passe à ReverseProxy.Rewrite : le port du moteur est relu à chaque requête au lieu d'être figé à la construction — il visait l'ancien port dès qu'on changeait PORT, jusqu'au redémarrage de Loki. - withLocalAuth (relay_link.go) n'injecte plus la clé de pilotage sur /v1 : elle aurait été refusée par la garde, et surtout relayée au moteur. Le trafic du tunnel est marqué (en-tête effacé avant d'être posé, sinon un client le forge) et la surface y reste fermée tant que oai_public est faux — la promesse du tunnel est tenue. Adresse affichée - web_public_url.go : normalisation d'une adresse publique saisie à la main (schéma ajouté, /v1 recopié toléré, chemin refusé), origine de la requête via Host + X-Forwarded-Proto, et la règle de priorité entre les deux. - Le calcul quitte le navigateur pour le serveur : c'est la concaténation côté client qui produisait l'adresse fantôme. Interface - Le panneau perd l'interrupteur ajean.link et l'interrupteur d'écoute LAN — ce dernier n'a plus d'objet, et deux interrupteurs pour « rendre l'IA joignable » était la confusion à lever. La route /api/network et `loki network` restent pour qui veut exposer le moteur en direct. - Il gagne un champ « adresse publique » (facultatif, pour le reverse proxy) et un avertissement rouge tant qu'aucune clé n'est définie — l'endpoint est maintenant ouvert PARTOUT où l'interface l'est, ça ne se dit pas à voix basse. Le démarrage de `loki web` le crie aussi. Vérifié bout en bout sur le serveur réel : liste des modèles à travers Loki avec la clé (200), sans la clé (401), et complétion en streaming dont les tokens arrivent espacés de 120 ms — le flux traverse bien le double proxy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
9c7f5283cf |
Flèche d'envoi : la centrer pour de bon
La flèche était décalée de 8 px vers la gauche dans son pavé — mesuré, pas supposé : centre du bouton 1339, centre du dessin 1331. La cause n'était pas dans la feuille de style mais dans le JS. syncSendBtn montrait et cachait les deux boutons avec `style.display = 'inline-block'` ; ce style INLINE l'emportait sur le `display:flex` de la règle commune. Le bouton gardait donc sa boîte de 34 px, mais son icône de 18 px se collait au bord gauche, `justify-content` n'ayant plus rien à centrer. Invisible tant que l'icône était un glyphe de texte (qui remplit sa ligne), flagrant depuis qu'elle est un SVG de taille fixe. L'état « génération en cours » passe désormais par un attribut sur <html> (data-busy) et c'est la feuille qui décide lequel des deux boutons s'affiche — comme pour le thème, la barre latérale et le panneau Fichiers. Le style inline `display:none` du bouton d'arrêt disparaît aussi du gabarit. Vérifié au pixel dans les deux états : écart nul entre le centre du bouton et celui du dessin, pour les trois commandes de la barre (dépôt, fichiers, envoi) comme pour le bouton d'arrêt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
fef8cca2c4 |
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 |
||
|
|
82136d6e0d |
Palettes officielles Stitch, géométrie et moniteur relevés
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
|
||
|
|
18edc9d3ed |
Palette relevée sur la maquette v1 (claire et sombre)
Vérification faite contre la source : le projet Stitch « Loki AI Chat Modern
Redesign v1 », écrans clair et sombre. Les couleurs sont désormais MESURÉES sur
la maquette, plus approchées à l'œil — et elles posent une hiérarchie de
surfaces que l'intuition inverse :
maquette clair maquette sombre
barre latérale #E4E8E9 #0F1829 ← la plus sombre des trois
fil #EFF3F4 #1E2834
carte de réponse #E5E9EA #253141 ← s'écarte du fil
carte de saisie #EFF3F6 #1F2734 ← la plus claire
bulle utilisateur #7EA58A #334257
ligne active #D8DCDF #22293B
piste de jauge #D5D9DC #1E2738
Ce qui change concrètement :
- La barre latérale était la surface la plus CLAIRE (#FAFBFA) ; elle est la plus
sombre. Idem pour la carte de réponse, qui était quasi blanche alors que la
maquette la fait descendre sous le fil. En sombre elle monte au-dessus : dans
les deux cas la carte se détache en s'éloignant du fond, jamais en allant vers
le blanc.
- La bulle de l'utilisateur ne suit plus l'accent. Elle a son rôle propre
(--user-bg) : sauge en clair, ARDOISE en sombre — la maquette réserve le vert
aux jauges dès que le fond est sombre, ce que le vert unique ignorait.
- --accent redevient une couleur d'ENCRE (liens, icônes, sélection), lisible sur
les deux fonds ; --accent-fill porte les aplats. Sans cette séparation, la
sauge claire de la maquette (#7EA58A) donnait des icônes à 2:1 de contraste.
- La sélection dans la liste des discussions est un aplat neutre (#D8DCDF) et
non une teinte verte : la maquette n'y met pas de second accent.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd
|
||
|
|
040c32a6d6 |
Blocs de code : un fond qui se détache de la carte de réponse
Depuis que la réponse de l'IA est une carte, le bloc de code avait exactement son ton (#F4F6F5 dans une bulle #F6F8F7) : l'encadré disparaissait dans la bulle, et un long prompt collé dans une réponse ne se distinguait plus du texte qui l'entoure. --code-bg est désormais nettement plus soutenu que --bubble, dans les deux variantes, avec un filet net. Le code EN LIGNE suit la même règle. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
6efd2a362e |
Barre de saisie cohérente et étiquettes d'identité stables
Deux retours, deux vrais défauts. Boutons de la saisie Ils avaient trois générations de règles empilées — pilule large « Envoyer », aplat noir, aplat sauge — si bien que chaque bouton tirait sa taille, sa forme et son alignement d'une couche différente : rond pour l'envoi et l'arrêt, carré de 15 px pour l'icône stop contre 16 pour ses voisines, et un align-self:center sur deux boutons seulement (les deux autres tombaient au bas de la pilule). Une seule règle les décrit désormais : 34x34, coins doux de 10 px — jamais un rond, la maquette n'en a pas —, icône de 18 px centrée, fond seulement au survol. Seule la couleur les distingue : atténué pour joindre et fichiers, sauge pour envoyer, rouille pour arrêter. Étiquettes des bulles Une bulle changeait de nom en cours de route. Envoyée en direct, elle s'appelait « USER » (confirmPending réécrivait l'étiquette en dur) ; rejouée au chargement, elle portait l'avatar et le prénom. Même chose côté réponse : labelTokens remplaçait « Loki » par « assistant · 75 tok · 21.5 tok/s » pendant la génération, et le nom ne revenait qu'au rechargement de la page. L'étiquette porte maintenant l'identité DANS TOUS LES CAS ; les mesures de génération vont dans la ligne dédiée, à l'endroit exact où les stats de fin de tour les écrivent déjà. L'envoi en cours se lit à la bulle grisée et à son infobulle, plus à un libellé qui écrase le prénom. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
276a8287cb |
Refonte : modèle chargé, attribution, ombres, flèche, favicon
Retours sur la refonte, cinq corrections. Modèle affiché Le sélecteur de l'en-tête ne montrait que le PRESET actif — or un preset n'est « actif » que si son empreinte correspond exactement à la configuration. Une configuration écrite à la main, héritée de l'image ou modifiée d'un cran depuis le dernier preset n'en désigne aucun, et l'en-tête restait muet alors qu'un modèle tournait. Il retombe désormais sur le nom du fichier de la configuration active (loadCfg le lui donne), et l'infobulle porte le chemin complet. Attribution « fork de AJEAN » revient sous le nom de l'app, dans l'en-tête de la barre latérale, au lieu du pied de la zone défilante. Ombres Les bulles du fil et la carte de saisie portent une vraie ombre douce (--shadow-card), comme la maquette — le filet de 1 px ne les décollait pas du fond. Bouton d'envoi Chevron sauge sur fond transparent, à la place de la pastille verte pleine : c'est le dessin de la maquette, et il suit alors la même charte que ses voisins (dépôt, fichiers). Favicon Carré sauge (#5E7F5A) au lieu du carré noir, aux quatre endroits qui le dessinent : favicon SVG, icône d'accueil iOS, icône du .exe (icon.ico) et PNG macOS — tous rendus depuis sys_brand_icon.go, seule source du dessin. La variante « template » de macOS reste noire : le système ne lit que son alpha. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
f99e5b59b8 |
Refonte de l'interface : direction « Sober Tech »
Reprise du design d'après la maquette fournie, sans changer d'architecture : l'UI reste du HTML/CSS/JS assemblé dans le binaire (voir plus bas). Charte - Palette ardoise + sauge en remplacement des bruns « Terre ». Deux variantes : claire par défaut (celle de la maquette) et « Deep Dark » (fond #0F172A, cartes #1E293B), à un clic depuis l'en-tête. Une variable --on-accent porte ce qui s'écrit sur les aplats de sauge, pour que le contraste tienne dans les deux variantes. - Typographie Inter (interface) + JetBrains Mono (code, chiffres, chemins), en sous-ensemble latin, embarquées comme l'étaient Bricolage et IBM Plex Mono — toujours aucune requête vers un service de polices. - Boutons sans contour, fond au survol, léger enfoncement au clic. - Coque à plat : plus de cartes flottantes séparées par une gouttière, la barre latérale est collée au bord et le fil occupe tout le reste. Coque - Barre d'en-tête : titre de la discussion + sélecteur de modèle. Changer de preset demandait d'ouvrir la barre latérale et de descendre jusqu'aux presets ; c'est désormais une liste déroulante, toujours visible. - Barre latérale en trois zones — marque, contenu défilant, moniteur machine — et escamotable d'un clic sur grand écran. Les discussions y passent au premier plan, avec une recherche textuelle (filtrage côté client : la liste est déjà chargée, sans les messages) ; les réglages descendent sous un repli unique, d'où openDetails(), qui déplie aussi les parents. - Moniteur machine en pied de colonne : une jauge par ressource (charge GPU, puis VRAM et température en détail, mémoire vive), sauge jusqu'à 75 %, ambre puis rouille quand la machine sature. Il remplace la section « Machine », qui disait la même chose en plus verbeux et en plus loin. - L'option « barre latérale escamotable » disparaît d'Apparence : elle faisait de la barre un tiroir posé SUR la conversation, là où le bouton d'en-tête l'escamote pour de bon. Deux mécanismes concurrents pour la même intention. Fil et saisie - La réponse de l'IA redevient une carte, la question un aplat de sauge : sur un fond de fil coloré, du texte nu flottait sans ancrage. - Saisie en pilule, ses outils (joindre, fichiers, envoyer) dans la barre elle-même. L'envoi est une pastille ronde à flèche ; le mot « Envoyer » reste porté par aria-label et l'infobulle. Corrections croisées - L'heure d'une discussion prenait la moitié de la ligne (flex:1 hérité de `.preset>span`), le titre était tronqué au premier mot. - Une liste déroulante posée directement dans une ligne de modale débordait sous son intitulé quand sa valeur était longue (chemin de destination). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
7310c84129 |
Bouton d'envoi : « Envoyer » au lieu de « send »
Le reste de l'interface est en français ; seul ce bouton était resté en anglais. L'identifiant #send et la fonction send() ne changent pas — c'est le libellé affiché qui est traduit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
001d633750 |
Un dossier de fichiers par discussion
Les fichiers du chat vivaient dans un pot commun : on changeait de discussion et on revoyait les mêmes pièces jointes, et supprimer une discussion laissait derrière elle tout ce qu'on y avait déposé ou fait écrire à l'agent (seules ses captures, déjà rangées par identifiant, partaient avec). Chaque discussion a désormais son dossier, <workspace>/discussions/<id>/ : - les dépôts (uploads/), les captures (captures/) et ce que l'agent écrit y atterrissent ; le shell et les chemins relatifs du modèle y sont résolus ; - le panneau Fichiers s'ouvre sur ce dossier et n'en sort pas, et se redessine quand la discussion change (bascule ou vidage, signalés par le flux SSE) ; - supprimer une discussion — ou la vider — emporte ses fichiers. Les deux gestes le disent maintenant avant de demander confirmation ; « clear chat » en demandait aucune. La racine du dossier de travail reste la borne de sécurité : les liens des anciens messages (uploads/x.pdf, captures/<id>/y.jpg) continuent d'ouvrir leur fichier par un chemin de repli. Au démarrage, une migration range les captures dans le dossier de leur discussion et rend chaque dépôt à la discussion qui le mentionne dans son journal ; ce que personne ne réclame reste à la racine, atteignable par le bouton « hors discussion » du panneau, qui disparaît une fois le ménage fait. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPyFxVHNAN9u5pVzSYMwjd |
||
|
|
00b9cc2aee |
Panneau Fichiers : accéder à ce que l'agent écrit
L'agent produit des rapports, des scripts, des exports et des captures dans son dossier de travail. On ne pouvait en récupérer un que si le modèle avait pensé à en mettre le lien dans sa réponse : tout ce qu'il écrivait sans le dire restait invisible, et faire le ménage demandait un shell. Un panneau de droite, ouvert et fermé par le bouton dossier du pied de carte, liste ce dossier avec navigation dans les sous-dossiers, téléchargement et suppression. Il pousse la conversation au lieu de la recouvrir — la largeur de lecture étant pilotée par --measure, le fil se resserre et se recentre tout seul. Sur téléphone c'est un tiroir, comme les réglages à gauche. Le socle existait : /api/chat/file téléchargeait déjà, workspaceRel bornait déjà les chemins, downloadWorkspaceFile gérait déjà le blob. Manquaient les deux verbes qui comptent, /api/chat/files pour lister et /api/chat/file/delete pour supprimer. Trois choix qui méritent d'être dits : - Un dossier affiche la taille de TOUT son contenu, pas celle de son inode : c'est ce qu'on libère en le supprimant, donc c'est le chiffre qui aide à décider. Le parcours est borné à 20 000 entrées pour que la liste reste instantanée si l'agent y dézippe quelque chose d'énorme. - La racine du dossier de travail est refusée à la suppression : l'agent y perd le dossier dans lequel il écrit, et « tout effacer » ne doit pas être à un clic. La suppression d'un dossier annonce le nombre de fichiers et le poids emportés avant de demander confirmation. - Le panneau ne se remplit qu'à l'ouverture. Un ReadDir récursif à chaque chargement de page pour un panneau fermé serait payé par tout le monde. Ouvrir le panneau rétrécit la conversation SANS déclencher de `resize` : la gouttière de barre de défilement mesurée dans --sbw resterait celle de l'ancienne largeur et la carte de saisie serait décalée du fil. On resynchronise donc explicitement ; la hauteur, elle, est déjà suivie par un ResizeObserver. Vérifié. Six tests Go sur le bornage, qui est la partie sensible — ces routes suppriment sur disque à partir d'un chemin venu du navigateur : « .. », chemins absolus, racine, et un lien symbolique posé dans le dossier de travail sont tous refusés, et le fichier voisin survit. Puis au navigateur, en clair et en sombre : listing trié dossiers d'abord, taille récursive, descente et fil d'Ariane, téléchargement réel (l'événement download porte bien rapport.md), suppression confirmée puis disparition de la ligne, persistance de l'état ouvert, tiroir mobile avec voile, et la saisie reste alignée au pixel sur le fil (0 px des deux côtés) une fois le panneau ouvert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VueWA9xcYadaYq65tBisix |
||
|
|
133b30421a |
Postes distants : retirer le bouton oublié du composeur
Le retrait de l'accès distant s'était arrêté à mi-chemin. La section « Accès distant » de la barre latérale et son module 13-remote.js ont bien disparu au commit |
||
|
|
26e93f43ef |
Chercher et installer un modèle depuis Loki
Installer un modèle demandait d'aller sur huggingface.co, de naviguer dans l'arborescence d'un dépôt et de coller un lien à la main. Rien ne disait si le fichier tiendrait en mémoire, et surtout rien ne reliait un modèle à SON projecteur vision : c'est ainsi qu'un mmproj-Qwen3VL-8B s'est retrouvé configuré pour un Qwen3.8-27B — deux modèles sans rapport, moteur qui démarre et ne voit rien. La recherche Hugging Face arrive dans l'éditeur de preset. Elle ne remonte que les dépôts GGUF ; déplier un dépôt montre ses quantifications avec leur taille et un verdict mémoire, et propose le projecteur vision DU MÊME DÉPÔT — le seul qui corresponde. Quand le dépôt n'en publie pas, Loki le dit au lieu d'aller en chercher un ailleurs. Le nouveau code ne télécharge rien : il produit des URL que le chemin existant consomme tel quel (normalizeHFURL, shardURLSet, sonde d'espace disque, reprise et annulation). Une file d'attente enchaîne modèle puis projecteur — le serveur ne mène qu'un transfert à la fois, et le champ Vision ne se remplit que si le modèle est déjà sélectionné. Trois familles de .gguf cohabitent dans un dépôt et ne veulent pas dire la même chose : mmproj-* (projecteur), mtp-* (poids de décodage spéculatif) et le modèle. Les deux premières ressemblent à un modèle ; les proposer en vrac, ce serait offrir de lancer llama-server sur un encodeur d'images. Les tranches d'une famille sont repliées en une entrée de taille TOTALE : annoncer 15 Go pour un modèle qui en occupe 45 promet une place qui n'existe pas. Le verdict mémoire s'appuie enfin sur le GPU. detectHardware() ne renvoyait que la RAM système alors que detectGPUs() existait déjà : sur un serveur à carte NVIDIA, le verdict se prononçait sur la mauvaise grandeur. Le coût du cache KV reste une estimation assumée — l'exact demanderait de parser l'en-tête GGUF — et l'interface l'annonce comme telle plutôt que d'afficher un chiffre faussement sûr. Le catalogue ajean.link disparaît. Sa route n'avait aucun consommateur (l'écran d'accueil qu'elle attendait n'a jamais existé), son repli embarqué proposait du Qwen2.5 de 2024, et un fork qui laisse le serveur de l'amont décider de ce qu'il propose n'est pas vraiment un fork. Une piste écartée en cours de route : marquer les dépôts « vision » d'après les tags Hugging Face. Ils mentent — des deux dépôts GGUF de Qwen3.8-27B qui publient tous deux un mmproj, seul ggml-org est taggé image-text-to-text. Une pastille sur l'un et pas sur l'autre aurait été pire que rien. La vision est donc déduite de la seule source qui ne se trompe pas : la présence d'un mmproj-*.gguf dans l'arborescence. Vérifié : 8 tests unitaires sur les arborescences réelles des deux dépôts, puis au navigateur contre le vrai Hugging Face — 25 dépôts trouvés, 3 quants listés sans aucun mtp ni mmproj, projecteur Q8_0 proposé et coché, verdicts affichés avec leur explication, modale dans l'écran. L'ordre de la file (modèle puis projecteur) est testé en interceptant les appels, sans transfert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VueWA9xcYadaYq65tBisix |
||
|
|
1fa8452e44 |
Le fil ne passe plus sous la carte de saisie
Sur écran large, la fin de la conversation était recouverte par la carte de
saisie et aucun défilement ne la ramenait : le fil réservait 8 px sous lui
là où le composeur en occupe ~160.
styles.css réservait bien la hauteur (#chat{padding-bottom:170px}), mais deux
règles POSTÉRIEURES dans la feuille — ajoutées par le redesign « Terre » puis
par le correctif de largeur de lecture — réécrivaient `padding` en raccourci
sous @media (min-width:721px), ce qui remet padding-bottom à la valeur du
raccourci. Sous 721 px la règle en raccourci est déclarée AVANT, d'où un bug
invisible sur mobile. Ces règles passent en longhands (padding-top +
padding-inline), et un commentaire dit pourquoi le raccourci est proscrit ici.
La réserve devient mesurée au lieu d'être devinée : syncComposer() publie
--composer-h (hauteur réelle du composeur) via un ResizeObserver, à côté de
syncGutter() qui fait déjà ce travail pour --sbw. Une constante redevenait
fausse dès que la saisie grandit ou qu'une pièce jointe s'affiche. Le
scrollbtn suit la même variable. env(safe-area-inset-bottom) disparaît du
calcul : il est déjà dans la hauteur mesurée, le compter deux fois creusait
un trou en PWA iOS.
Le voile de fondu au-dessus de la carte s'exprimait en % de la hauteur TOTALE
du composeur : l'opacité pleine tombait sous le bord haut de la carte, et le
texte restait lisible au ras du bord — l'impression de « passer derrière ».
Une variable --fade pilote désormais la bande réservée ET la course du
dégradé, qui devient donc opaque exactement au bord de la carte.
Mesuré au navigateur, du 380 px au 2000 px, au repos / saisie 10 lignes /
pièce jointe en attente : écart de 46 à 52 px partout. En réinjectant
l'ancienne règle : -119 px, bug reproduit. La largeur de lecture est
inchangée (1080 px de contenu à 2000 px de fenêtre).
Barre latérale : la discussion sélectionnée porte un cadre sauge complet et
fin (1 px) au lieu d'un liseré de 3 px sur le seul bord gauche. La bordure
transparente est portée par tous les états, la sélection ne décale donc pas
le contenu.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VueWA9xcYadaYq65tBisix
|
||
|
|
3740b881a8 |
Hauteur : bulles techniques en rangée, sections groupées, accès distant retiré
Trois gains de place, mesurés au navigateur en 1500×900. - Raisonnement, outils et mémoire se posent CÔTE À CÔTE dans une rangée qui déborde à la ligne : un tour d'agent en enchaîne parfois dix, et empilées elles repoussaient la réponse hors de l'écran alors qu'elles ne portent qu'une étiquette tant qu'elles sont repliées. Mesuré : 8 bulles occupent 2 lignes au lieu de 8. Dépliée, une bulle reprend toute la largeur — son contenu ne doit pas être comprimé dans une colonne étroite. - Les sections de la barre latérale sont réunies en DEUX cartes (réglages d'IA / réglages d'application) au lieu d'une carte chacune : onze arrondis et autant de gouttières faisaient déborder la colonne. Mesuré : 876 px de contenu pour 876 px visibles — plus d'ascenseur. - Section « Accès distant » supprimée, ainsi que son module JS (13-remote.js) et son chargement : elle pilotait le tunnel ajean.link, sans objet ici. Le code serveur du relais reste en place mais inerte (aucun jeton) : le retirer créerait des conflits à chaque reprise de l'amont. |
||
|
|
c90a2d86eb |
Largeur de lecture : suivre le panneau au lieu d'une colonne étroite centrée
Sur un écran large, le fil se réduisait à ~590 px de texte entouré de vide : la mesure était plafonnée à 760 px et centrée sur la fenêtre, alors que la charte fait courir le texte sur toute la largeur du panneau. Elle était en plus codée à TROIS endroits avec des valeurs différentes (450 / 380 / 360 px), donc condamnée à diverger entre le fil et la carte de saisie. Une seule variable --measure les pilote désormais tous les deux. La borne haute reste : au-delà, l'œil perd le début de la ligne suivante. 1080 px ≈ 110 caractères — large, mais encore lisible. Mesuré au navigateur en 2000 px : réponse 1050 px (contre 589), question 788 px (75 %, conforme à la charte), carte de saisie alignée sur le fil. |
||
|
|
e7a3bcca1f |
Direction « Terre » : nouvelle identité visuelle
Reprise de la maquette fournie (LOKI — direction Terre) : bruns chauds, vert sauge, argile, et des surfaces en CARTES arrondies séparées par une gouttière au lieu d'une colonne à filets. - Palette transposée sur les jetons EXISTANTS (--bg, --panel, --accent…) et non par réécriture des 1200 lignes : les 300 usages de variables suivent d'eux-mêmes. Rôles clarifiés — sauge = sélection et action, argile = chiffres et avertissements, terre cuite = arrêt et danger. - Polices Bricolage Grotesque + IBM Plex Mono EMBARQUÉES dans le binaire et servies par Loki (/fonts/), pas par Google Fonts : une instance locale ou coupée d'internet doit s'afficher correctement, sans qu'un tiers apprenne qui consulte l'interface. Sous-ensemble latin, 106 Ko au total. - Structure en cartes : barre latérale en pile de cartes, colonne de chat en panneau unique, carte de saisie posée un ton au-dessus. Liseré sauge sur la discussion active, bulle de question au coin bas-droit rentré. - Bouton d'envoi en sauge : il reprenait --text, donc un brun indistinct du reste ; l'arrêt reste en terre cuite. Bornage au bureau (min-width:721px) : sur téléphone la barre latérale est un tiroir plein écran et le fil occupe toute la largeur — des cartes n'y grignoteraient que des pixels utiles. Vérifié au navigateur, thèmes clair ET sombre, plus un rendu 390 px : polices réellement chargées (200 sur les trois woff2), aucun débordement horizontal, tiroir mobile inchangé. |
||
|
|
4854ae3784 |
Passe de qualité : contexte explosé par les captures, sonde vision, courses
Le journal moteur de production a livré la vraie cause première des 500 en cascade : « request (55407 tokens) exceeds the available context size (32768) ». Le message multimodal qui relaie une capture d'écran au modèle était PERSISTÉ dans l'historique ; son base64 (des dizaines de milliers de tokens) repartait à chaque tour, et la conversation dépassait définitivement la fenêtre — plus aucun tour ne passait, et l'exception Jinja du rattrapage (corrigée au commit précédent) masquait tout. - L'image devient ÉPHÉMÈRE : jointe au tour en cours, jamais à l'historique. Le modèle la regarde maintenant ; sa description textuelle, elle, reste. - stripImageParts guérit les conversations déjà empoisonnées au chargement et à la bascule : parties image retirées, texte aplati. - engineSeesImages : l'image n'est envoyée que si llama-server DÉCLARE la vision (/props, modalities.vision, cache 10 s). La clé MMPROJ ne suffit pas — projecteur d'un autre modèle ou modèle sans vision (gpt-oss), le gabarit sérialise le base64 en texte. La description de l'outil suit le même état : ne jamais promettre une image qui n'arrivera pas. Deux défauts trouvés à l'analyse, au passage : - renderBody reconstruit le DOM à chaque delta du streaming : une image déjà affichée était RE-TÉLÉCHARGÉE à chaque token arrivé après elle. Cache de blobs par URL, une seule requête par capture. - les opérations de discussions (création, bascule, renommage, suppression) entrelaçaient leurs lectures-écritures d'index sous requêtes simultanées : sérialisées par un verrou dédié. Tests : historique guéri (aplati sans l'image), transmission conditionnée à la sonde, description alignée ; suite complète, vet, staticcheck verts. |
||
|
|
8b76d8de9c |
Prénom : écarter l'autocomplétion des gestionnaires de mots de passe
Le champ « Ton prénom » n'annonçait rien sur sa nature : les gestionnaires le prenaient pour un identifiant de connexion et recouvraient la saisie d'une liste de comptes enregistrés (192.168.1.16 / admin…), rendant le champ pénible à remplir. autocomplete="off" ne suffit pas seul — la plupart des gestionnaires l'ignorent délibérément sur ce qu'ils croient être un formulaire de connexion. On ajoute donc les marqueurs qu'ils respectent réellement : data-1p-ignore (1Password), data-lpignore (LastPass), data-form-type="other" (Dashlane), et un name explicite qui n'évoque ni user ni login. |
||
|
|
72a8925cb4 |
Avatars et prénoms : les afficher pour de bon (ils étaient masqués par le CSS)
Les libellés étaient bien construits — mon test précédent vérifiait le HTML
produit, pas le style appliqué — mais la feuille de style les masquait tous :
#chat .msg .label{display:none}
C'était un choix justifié de l'amont, où le libellé ne disait que « user » ou
« assistant », deux mots que l'alignement des bulles rend redondants. Depuis
qu'ils portent un avatar et un prénom, ils identifient réellement
l'interlocuteur : on les réaffiche pour user et assistant, en ligne et
discrets, aligné à droite côté question. Les bulles techniques non repliables
restent muettes.
Vérifié dans un navigateur : display=flex, boîte non vide, textes rendus
« 🙂 Michael » et « 🦊 Loki » — cette fois sur le style calculé et sur une
capture d'écran, pas seulement sur le DOM.
|
||
|
|
6fe94b9a8d |
Captures de pages web (Playwright) affichées dans le fil
Outil web_screenshot : Chromium piloté par Playwright photographie une page RENDUE (JavaScript exécuté) dans le dossier de travail, et rend au modèle la ligne markdown exacte à recopier — lui laisser composer l'URL reviendrait à lui faire inventer un chemin, donc une image cassée. INDÉPENDANT DE LA VISION, souvent confondu : visionEnabled() (clé MMPROJ) ne décide que d'une chose, l'envoi d'images AU MODÈLE. Capturer et afficher ne passent pas par le modèle — sans projecteur, l'agent photographie sans regarder, ce qui suffit à illustrer une conversation. - route /api/chat/image : sert UNIQUEMENT des images du dossier de travail, en ligne. handleChatFile force le téléchargement de tout pour qu'un .html du modèle ne s'exécute pas dans l'origine de l'UI ; ici la même règle est tenue autrement — type déduit du CONTENU (pas de l'extension), nosniff, et CSP default-src 'none'. Un faux .png contenant du HTML est refusé en 415. - UI : une balise <img> ne peut pas porter d'en-tête Authorization, or /api/* exige la clé dès qu'elle est définie. Les images sont donc récupérées par fetch authentifié puis posées en blob:. - l'outil n'est déclaré au modèle QUE si Playwright est réellement présent : annoncer un outil absent envoie le modèle en boucle de réessai. - Node 22 (NodeSource) au lieu du Node 18 d'Ubuntu, exigé par Playwright et par la plupart des serveurs MCP. PLAYWRIGHT=0 bâtit une image sans navigateur. - CONFIG ACTIVE affichait « llama.cpp personnalisé » pour le moteur de l'image : il annonce maintenant « llama.cpp de l'image ». Description de l'outil tenue au plus court : TestSystemPromptStaysLean a attrapé le dépassement du budget de préambule (7888 car pour 7500). Vérifié dans l'image : capture réelle d'une page, servie en image/png avec CSP ; faux PNG rejeté (415) ; évasion du workspace bloquée (403). Playwright ajoute 816 Mo à l'image. |
||
|
|
54c5eb1334 |
Identité dans le chat, section Paramètres, logo agrandi
- Identité : prénom et avatars (emoji) affichés en tête des bulles, à la place des libellés « user » / « assistant ». Stockés dans les préférences SERVEUR, donc partagés entre appareils comme le thème. La liste d'emojis est servie par /api/prefs : pas de seconde copie côté client. Validation stricte — prénom nettoyé de ses caractères de contrôle et borné à 24 runes, avatar refusé s'il n'est pas dans la liste fermée. - Barre latérale : séparateur « Paramètres » qui regroupe Identité, Apparence, Accès OpenAI, Accès distant et Actions, sous les réglages d'IA (Machine, Presets, Mode agent, System prompt, Moteur). Un séparateur plutôt qu'un second niveau de repli, illisible dans une colonne aussi étroite. - Logo : 10 → 17 px, il était minuscule sur un écran dense. Le prénom vient de l'utilisateur : les libellés sont écrits en textContent, jamais en innerHTML. |
||
|
|
ecaa847439 |
NGL=auto : laisser llama.cpp ajuster les couches à la VRAM libre
Loki passait TOUJOURS -ngl, avec 999 comme repli quand le champ est vide : vider « Couches sur GPU » ne changeait donc rien. Or llama.cpp sait ajuster lui-même le nombre de couches à la mémoire libre (common_fit_params), mais y renonce dès que la valeur est imposée — « n_gpu_layers already set by user to 999, abort ». D'où, sur une carte trop juste pour le contexte demandé, des cudaMalloc en échec, un repli partiel sur le CPU, et un débit effondré alors que le GPU tourne à 100 %. NGL=auto n'envoie plus -ngl du tout. Le défaut reste 999 : omettre le drapeau sur un llama.cpp antérieur à cet ajustement le ferait tourner 100 % CPU. Le champ de l'UI passe en texte pour accepter « auto ». Vérifié : NGL=auto → aucun -ngl dans la ligne de commande ; NGL=999 → -ngl 999. |
||
|
|
2fbf86cdde |
Historique : plusieurs discussions, et mise à jour par l'image en conteneur
L'amont ne connaît qu'un fil unique (bkChat/conversation) que « clear chat »
effaçait définitivement. On garde toute la machinerie (un seul conv en
mémoire, mêmes flux SSE, même compactage) mais rangée par discussion :
bkChat/index liste des discussions (métadonnées seules)
bkChat/active discussion ouverte, partagée par tous les appareils
bkChat/conv:<id> état complet d'une discussion
Basculer réutilise le mécanisme d'epoch du reset : les abonnés SSE reçoivent
{reset:true} et rejouent le nouveau fil — aucun code de rendu à toucher. Le
fil unique existant est repris comme première discussion au premier
démarrage, et sa clé d'origine est laissée intacte.
- routes /api/conversations (liste, new, switch, rename, delete)
- barre latérale : liste (titre déduit du 1er message, date, nb d'échanges),
bouton +, renommer, supprimer ; lignes construites en DOM et non en
innerHTML, les titres venant de messages utilisateur
- suppression de la dernière discussion : convCreate et non convNew, qui
aurait réenregistré celle qu'on vient d'effacer
Le bouton « Vérifier les mises à jour » répondait « GitHub a répondu 404 » :
il interrogeait les releases du dépôt du fork, qui n'en publie aucune. En
conteneur, remplacer le binaire n'a de toute façon pas de sens — l'UI, l'API
et la CLI renvoient désormais « docker compose pull ».
Vérifié en conteneur : création, bascule, renommage (titre accentué avec
< > &), suppression de l'active puis de la dernière ; /api/update et
loki update renvoient la note Docker.
|
||
|
|
1f24d8ccf5 |
Sélecteur de moteur par modèle : « Image » en conteneur, et mention du fork
Le sélecteur de l'éditeur de modèle proposait encore « Précompilé » et « Compilé » — deux installations que Loki ne fait pas en conteneur : cliquer répondait « installez d'abord llama.cpp… ». Pire, le moteur de l'image ne correspondant à aucun des deux, il était classé « Personnalisé ». En conteneur, ces deux options laissent place à « Image » (sélectionnée par défaut, y compris pour un preset sans BIN) ; « Personnalisé » reste pour un binaire déposé dans /data/backends. Le moteur de l'image est désormais déclaré par l'image elle-même via LOKI_ENGINE_BIN (Dockerfile + entrypoint) au lieu d'être deviné d'après le chemin : /api/llamacpp expose provided et provided_bin. Attribution : sous le nom Loki, « fork de AJEAN » avec lien vers le dépôt d'origine. Vérifié dans l'image : provided=true, provided_bin=/app/llama-server, BIN semé identique ; build, vet et tests verts. |
||
|
|
5b434a4a59 |
UI : moteur fourni par l'image, et journal du moteur repliable
Le panneau « Moteur » proposait d'installer llama.cpp alors que l'image le fournit déjà (/app/llama-server) : cliquer lançait une compilation qui échouait sur « outils manquants : cmake », l'image n'embarquant ni cmake ni compilateur. - engineProvided() : vrai en conteneur quand BIN désigne un exécutable existant HORS de LOKI_HOME (donc ni dépôt cloné, ni précompilé, ni backend custom, qui sont gérés par Loki) ; exposé en clé 'provided' de /api/llamacpp - les routes install / install-custom / update refusent en 409 avec un message explicite au lieu de partir en build - l'UI affiche l'état réel (chemin du binaire) et masque les trois modes Le journal du moteur n'était repliable que par un second clic sur la pastille d'état — sans chevron ni titre cliquable, contrairement à toutes les autres sections de la barre latérale. Il devient un <details><summary> comme les autres ; la pastille reste un raccourci et le chargement suit l'événement toggle, quel que soit le moyen d'ouverture. Vérifié dans l'image : provided=true, config_bin=/app/llama-server, POST /api/llamacpp/install → 409 explicite, <details id="svc-log-box"> servi. |
||
|
|
bb9aa9f559 |
Attribution du fork, README, logo UI et workflow GHCR
- NOTICE.md : Loki est un fork d'AJEAN (nathaninline, MIT), liste des modifications ; LICENSE amont conservée à l'identique. - README réécrit : bandeau fork, architecture conteneur, démarrage Docker, procédure Unraid (plugin Nvidia Driver), différences avec l'amont. - UI : le logo pixel-art épelle désormais LOKI (il épelait encore AJEAN), infobulle d'attribution sur la marque ; index.html régénéré. - Workflow GHCR : libération d'espace disque du runner (l'étape CUDA devel ne tient pas dans les ~14 Go libres), build-args CUDA_ARCHS/LOKI_VERSION, cache GHA en mode min (plafond 10 Go). |
||
|
|
de6551a153 |
Rebaptise AJEAN en Loki (fork, lignée conservée)
- module github.com/R0m1k3/Loki, cmd/loki, internal/loki (package loki) - LOKI_HOME, LOKI_MODEL_DIRS, LOKI_SERVICE, LOKI_DL_CONNS ; /etc/loki ; units loki-engine / loki-ui ; binaire et aide CLI - updateRepo pointe sur R0m1k3/Loki (l'auto-update ne tirera plus les binaires AJEAN amont) Conservé à l'identique : le domaine ajean.link (service de tunnel amont), les littéraux de migration 0.7.x (migrate_07.go), RELEASE_NOTES.md et LICENSE (historique et licence de l'amont). go build/vet/test : verts. |