Purge du code de compatibilite jean, base bbolt et arborescence ajean

- suppression des migrations d'agencement, d'unites et de dossier de donnees
- store bbolt unique (ajean.db) : config, prefs, conversation, cles, drapeaux
- arborescence backends/ bin/ presets/ memory/ models/ workspace/
- plus aucune variable, unite ou asset nomme jean
This commit is contained in:
nathaninline committed 2026-08-07 16:51:36 +02:00
1 parent 081a88c846
commit 2455d9f402
78 files changed
+1160 -2293

No files matched your search

+13 -10
View File
@@ -239,24 +239,23 @@
</div>
</details>
<details id="lc-details"><summary><span class="stitle">Moteur</span></summary>
<div class="subhead">Installation</div>
<!-- Une seule carte : état, choix du mode et journal d'installation sont le
même sujet. Les trois modes sont des LIGNES de cette carte (elles
étaient des cartes dans une carte). -->
<div class="slist">
<!-- Texte de départ IDENTIQUE (à la variante près) à celui posé après le
chargement : un simple « … » faisait grandir la carte sous les yeux dès
que l'état arrivait. -->
<div class="rowbody"><div id="lc-status" class="lc-head muted">Installez les moteurs ici. Vous choisissez lequel utiliser en éditant un modèle.</div></div>
<!-- La pastille « conseillé » n'est PAS écrite ici : le bon choix dépend de
la machine (voir lcRenderReco). Sur Linux + NVIDIA le précompilé ne
donne que du Vulkan — llama.cpp ne publie aucun binaire CUDA Linux —
alors que compiler donne du CUDA natif. -->
<div class="lc-modes">
<div class="lc-mode" id="lc-mode-fast" onclick="lcPick('fast')">
<div class="lc-mode-t">llama.cpp précompilé <span class="lc-reco">conseillé</span></div>
<div class="lc-mode-d">binaire officiel téléchargé tel quel, prêt en ~2 min</div>
<div class="lc-mode-t">llama.cpp précompilé <span class="lc-reco" id="lc-reco-fast" hidden>conseillé</span></div>
<div class="lc-mode-d" id="lc-desc-fast">binaire officiel téléchargé tel quel, prêt en ~2 min</div>
<div class="lc-mode-state" id="lc-fast-state"></div>
</div>
<div class="lc-mode" id="lc-mode-opt" onclick="lcPick('opt')">
<div class="lc-mode-t">llama.cpp compilé</div>
<div class="lc-mode-d">compilé ici pour votre machine — plus rapide, plus long à installer</div>
<div class="lc-mode-t">llama.cpp compilé <span class="lc-reco" id="lc-reco-opt" hidden>conseillé</span></div>
<div class="lc-mode-d" id="lc-desc-opt">compilé ici pour votre machine — plus rapide, plus long à installer</div>
<div class="lc-mode-state" id="lc-opt-state"></div>
</div>
<div class="lc-mode" id="lc-mode-custom" onclick="openCustomBackends()">
@@ -489,6 +488,10 @@
<div id="m-hf-progress" class="pe-note" style="display:none"></div>
<button id="m-hf-cancel" class="pe-link" onclick="cancelDownload()" style="display:none">Annuler le téléchargement</button>
</div>
<div class="pe-row">
<span class="pe-row-l">Destination</span>
<span class="pe-selc"><select id="m-hf-dir"></select></span>
</div>
<div id="m-dirs-toggle" class="pe-row" style="cursor:pointer" onclick="toggleModelDirs()">
<span class="pe-row-l">Dossiers de modèles<span class="pe-sub">chercher les .gguf ailleurs (disque externe…)</span></span>
<span class="pe-row-c"><span id="m-dirs-caret" class="pe-caret"></span></span>
@@ -499,7 +502,7 @@
<input id="m-dir-path" class="pe-inp" placeholder="/media/disque-externe/models">
<button id="m-dir-add" onclick="addModelDir()" title="Ajouter ce dossier">+</button>
</div>
<div class="pe-note">Les téléchargements, eux, vont toujours dans le dossier AJEAN.</div>
<div class="pe-note">Ces dossiers servent aussi de destination de téléchargement.</div>
</div>
</div>
</div>
+46 -8
View File
@@ -153,7 +153,7 @@ async function openItem(kind, key){
document.getElementById('m-quant').value = currentQuantInTextarea();
populateSettings();
attachDownload(); // téléchargement encore en cours côté serveur ?
await Promise.all([populateBackend(), populateModelPicker()]);
await Promise.all([populateBackend(), populateModelPicker(), populateDlDirs()]);
if(seq !== openSeq) return;
}
// On lève le voile SANS transition (voir la note dans styles.css), puis on rend
@@ -252,7 +252,8 @@ async function populateModelDirs(){
const info = document.createElement('span');
info.className = 'muted';
const n = x.count >= 0 ? (x.count + ' modèle' + (x.count>1?'s':'')) : 'illisible';
info.textContent = '(' + n + (x.home ? ', dossier jean' : '') + ')';
const free = x.free >= 0 ? ', ' + fmtSize(x.free) + ' libres' : '';
info.textContent = '(' + n + free + (x.home ? ', dossier jean' : '') + ')';
row.append(p, info);
if(!x.home){
const del = document.createElement('button');
@@ -273,13 +274,13 @@ async function addModelDir(){
if(!r.ok){ toast('erreur : ' + (r.error||'')); return; }
inp.value = '';
toast('dossier ajouté');
await Promise.all([populateModelDirs(), populateModelPicker()]);
await Promise.all([populateModelDirs(), populateModelPicker(), populateDlDirs()]);
}
async function removeModelDir(p){
const r = await jpost('/api/models/dirs', {path: p, action: 'remove'});
if(!r.ok){ toast('erreur : ' + (r.error||'')); return; }
toast('dossier retiré');
await Promise.all([populateModelDirs(), populateModelPicker()]);
await Promise.all([populateModelDirs(), populateModelPicker(), populateDlDirs()]);
}
function onPickModel(){
const val = document.getElementById('m-model').value;
@@ -772,17 +773,50 @@ function resetDlUI(){
e.prog.style.display='none'; e.bar.style.display='none';
e.cancel.style.display='none'; e.btn.disabled=false;
}
// Remplit le sélecteur de destination avec les dossiers de modèles connus.
// L'espace libre est dans le libellé de l'option : c'est l'info utile au moment
// de choisir, et ça évite une ligne de plus dans une modale déjà chargée.
// Même source que « Dossiers de modèles » : ajouter un disque externe là-bas le
// rend aussitôt disponible ici.
let dlDirList = [];
async function populateDlDirs(){
const sel = document.getElementById('m-hf-dir');
if(!sel) return;
let d = {};
try{ d = await jget('/api/models/dirs'); }catch(_){ return; }
dlDirList = d.dirs || [];
const prev = sel.value;
sel.innerHTML = '';
for(const x of dlDirList){
const o = document.createElement('option');
o.value = x.path;
o.textContent = x.path + (x.free >= 0 ? ' — ' + fmtSize(x.free) + ' libres' : '');
sel.append(o);
}
if(prev && dlDirList.some(x => samePath(x.path, prev))) sel.value = prev;
}
async function startDownload(){
const url = document.getElementById('m-hf-url').value.trim();
if(!url){ toast('colle un lien .gguf'); return; }
const dir = (document.getElementById('m-hf-dir')||{}).value || '';
const e = dlEls();
e.btn.disabled = true;
e.prog.style.display = 'block';
e.prog.textContent = 'démarrage…';
e.prog.textContent = 'vérification de la taille et de l’espace disque…';
e.bar.style.display = 'block';
e.bar.className = 'pe-bar indet';
e.bar.firstElementChild.style.width = '';
const r = await jpost('/api/models/download', {url});
// Sonde d'abord : on connaît la taille exacte du fichier et l'espace restant
// AVANT d'écrire quoi que ce soit, plutôt que d'échouer à 90 % du transfert.
const p = await jpost('/api/models/download/probe', {url, dir});
if(!p.ok || !p.enough){
e.prog.innerHTML = '<span style="color:var(--err)">erreur : '+escHtml(p.error||'espace insuffisant')+'</span>';
e.bar.style.display = 'none'; e.btn.disabled=false;
await populateDlDirs();
return;
}
e.prog.textContent = fmtSize(p.size)+' à télécharger — '+fmtSize(p.free)+' libres';
const r = await jpost('/api/models/download', {url, dir});
if(!r.ok){
e.prog.innerHTML = '<span style="color:var(--err)">erreur : '+(r.error||'')+'</span>';
e.bar.style.display = 'none'; e.btn.disabled=false; return;
@@ -825,8 +859,12 @@ function watchDownload(fname){
e.prog.innerHTML = '<span style="color:var(--ok)">✓ '+fname+' téléchargé ('+fmtSize(st.done)+')</span>';
e.bar.className = 'pe-bar done'; e.bar.firstElementChild.style.width = '100%';
stop();
await populateModelPicker();
document.getElementById('m-model').value = fname; onPickModel();
await Promise.all([populateModelPicker(), populateDlDirs()]);
// Le modèle a pu atterrir hors du dossier jean : l'option porte alors le
// chemin complet, pas le simple nom de fichier.
const sel = document.getElementById('m-model');
const opt = Array.from(sel.options).find(o => samePath(baseName(o.value), fname));
if(opt){ sel.value = opt.value; onPickModel(); }
return;
}
const pct = st.total>0 ? st.done*100/st.total : 0;
+18 -6
View File
@@ -15,12 +15,7 @@ async function loadLlamacpp(){
const fastInstalled = !!pb.bin;
const optInstalled = !!s.bin;
const head = document.getElementById('lc-status');
if(!fastInstalled && !optInstalled){
head.innerHTML = 'Installez le moteur d\'IA. Le <b>précompilé</b> convient à presque tout le monde.';
} else {
head.innerHTML = 'Installez les moteurs ici. Vous choisissez lequel utiliser en éditant un modèle (bouton ⚙).';
}
lcRenderReco(s.reco);
lcRenderMode('fast', fastInstalled);
lcRenderMode('opt', optInstalled);
lcRenderCustomCard();
@@ -78,6 +73,23 @@ function lcChipOpen(){
lcChipSync(null);
}
// Place la pastille « conseillé » sur la carte que le SERVEUR recommande pour
// cette machine (voir recommendedMode) : le précompilé partout, sauf sur Linux
// avec une carte NVIDIA où il ne donnerait que du Vulkan. La raison est ajoutée
// à la description de la carte, pour expliquer plutôt que d'imposer.
function lcRenderReco(reco){
const mode = (reco && reco.mode) || 'fast';
for(const m of ['fast','opt']){
const badge = document.getElementById('lc-reco-'+m);
if(badge) badge.hidden = (m !== mode);
}
// La raison REMPLACE la description générique de la carte conseillée : elle
// dit déjà ce que fait l'option et pourquoi c'est le bon choix ici. (loadAll
// repasse par là, donc pas d'accumulation possible.)
const desc = document.getElementById('lc-desc-'+mode);
if(desc && reco && reco.why) desc.textContent = reco.why;
}
function lcRenderMode(mode, installed){
const card = document.getElementById(mode==='fast' ? 'lc-mode-fast' : 'lc-mode-opt');
const state = document.getElementById(mode==='fast' ? 'lc-fast-state' : 'lc-opt-state');