mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-23 06:58:14 +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
@@ -67,68 +67,86 @@
|
||||
<div id="cfg" class="slist">…</div>
|
||||
</details>
|
||||
<details><summary><span class="stitle">Presets</span><button class="preset-add" onclick="event.preventDefault();event.stopPropagation();openPreset('')" title="ajouter">+</button></summary>
|
||||
<div id="presets"></div>
|
||||
<div class="slist"><div id="presets" class="rowlist"></div></div>
|
||||
</details>
|
||||
<details><summary><span class="stitle">Mode agent</span><span id="agent-badge" class="sbadge"></span></summary>
|
||||
<div class="slist"><label class="switchrow"><span class="switch"><input type="checkbox" id="agent-toggle" onchange="toggleAgent()"><span class="slider"></span></span> activer le mode agent<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">⚠ active l'<b>accès machine</b> (shell complet via <b>bash</b> sur le serveur) — l'IA peut inspecter, lancer des scripts, écrire/lire/éditer des fichiers (timeout 30s, max 300s). Mémoire et accès internet ci-dessous ne fonctionnent que si le mode agent est activé.</span></span></label></div>
|
||||
<!-- Mémoire / Accès internet / Paramètres : sous-réglages du mode agent, en cartes. Grisés/inertes quand l'agent est off (voir setAgentGate). -->
|
||||
<div id="mem-block" class="gated-block">
|
||||
<!-- Sous-réglages du mode agent. MÊME gabarit que « Machine » / « Apparence » :
|
||||
étiquette de groupe nue, puis UNE carte de lignes (.slist). Le groupe
|
||||
entier est grisé/inerte quand l'agent est off (voir setAgentGate). -->
|
||||
<div id="mem-block" class="gated-group">
|
||||
<div class="subhead">Mémoire<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span id="mem-mode-desc" class="tip"></span></span></div>
|
||||
<label class="switchrow"><span>mode</span>
|
||||
<select id="mem-mode" onchange="setMemMode()" class="sctl">
|
||||
<option value="always">auto (proactive)</option>
|
||||
<option value="ondemand">sur demande</option>
|
||||
<option value="off">désactivée</option>
|
||||
</select>
|
||||
</label>
|
||||
<div id="mem-pages-bar" class="pages-bar" onclick="toggleMemPages()">
|
||||
<span class="pages-caret">▸</span>
|
||||
<span class="pages-title">Pages <span id="mem-count" class="mem-count"></span></span>
|
||||
<button class="addbtn" onclick="event.stopPropagation();openMem('')" title="ajouter une page mémoire">+ ajouter</button>
|
||||
</div>
|
||||
<div id="mem-pages-body" hidden>
|
||||
<input id="mem-search" class="mem-search" type="search" placeholder="filtrer la mémoire…" oninput="renderMemList()" style="display:none">
|
||||
<div id="mem-list" class="mem-list muted" style="font-size:12px"></div>
|
||||
<div class="slist">
|
||||
<label class="switchrow"><span>mode</span>
|
||||
<select id="mem-mode" onchange="setMemMode()" class="sctl">
|
||||
<option value="always">auto (proactive)</option>
|
||||
<option value="ondemand">sur demande</option>
|
||||
<option value="off">désactivée</option>
|
||||
</select>
|
||||
</label>
|
||||
<div id="mem-pages-bar" class="pages-bar" onclick="toggleMemPages()">
|
||||
<span class="pages-caret">▸</span>
|
||||
<span class="pages-title">Voir la mémoire <span id="mem-count" class="mem-count"></span></span>
|
||||
<button class="addbtn" onclick="event.stopPropagation();openMem('')" title="ajouter une page mémoire">+ ajouter</button>
|
||||
</div>
|
||||
<div id="mem-pages-body" hidden>
|
||||
<!-- Le champ de filtre garde son rembourrage de ligne ; la liste, elle,
|
||||
est à fleur de carte : ses pages sont des LIGNES séparées par un
|
||||
filet, pas une pile de petites cartes. -->
|
||||
<div id="mem-search-row" class="rowbody" style="display:none">
|
||||
<input id="mem-search" class="mem-search" type="search" placeholder="filtrer la mémoire…" oninput="renderMemList()" style="margin:0">
|
||||
</div>
|
||||
<div id="mem-list" class="mem-list rowlist muted" style="font-size:12px"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="net-block" class="gated-block">
|
||||
<div id="net-block" class="gated-group">
|
||||
<div class="subhead">Accès internet<span id="internet-badge" class="sbadge"></span><span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Ajoute les outils <b>web_search / web_open / web_read / web_grep</b>. Le moteur <b>intégré</b> ne demande aucune installation. Le moteur <b>Crawl4AI</b> exige un serveur que tu héberges toi-même (ex. <code>docker run -d -p 11235:11235 --shm-size=1g unclecode/crawl4ai:latest</code>) mais exécute le JavaScript des pages. Actifs seulement si le <b>mode agent</b> est actif.</span></span></div>
|
||||
<label class="switchrow"><span class="switch"><input type="checkbox" id="internet-toggle" onchange="toggleInternet()"><span class="slider"></span></span> donner internet à l'IA</label>
|
||||
<!-- Moteur web : « go » = intégré au binaire (rien à installer, pas de rendu
|
||||
JavaScript) · « crawl4ai » = serveur externe avec Chromium headless. -->
|
||||
<div class="row" style="margin-top:6px">
|
||||
<select id="web-engine" class="sctl" onchange="saveWebEngine()">
|
||||
<option value="go">Moteur Intégré</option>
|
||||
<option value="crawl4ai">Crawl4AI</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- Réglages propres à Crawl4AI : masqués avec le moteur intégré.
|
||||
URL et clé s'enregistrent en quittant le champ (onchange) : deux boutons
|
||||
« enregistrer » côte à côte n'apportaient rien et brouillaient la carte. -->
|
||||
<div id="crawl-fields">
|
||||
<div class="row" style="margin-top:6px">
|
||||
<input id="crawl-url" type="text" placeholder="http://localhost:11235" class="sctl" onchange="saveCrawlUrl()">
|
||||
<div class="slist">
|
||||
<label class="switchrow"><span class="switch"><input type="checkbox" id="internet-toggle" onchange="toggleInternet()"><span class="slider"></span></span> donner internet à l'IA</label>
|
||||
<!-- Moteur web : « go » = intégré au binaire (rien à installer, pas de rendu
|
||||
JavaScript) · « crawl4ai » = serveur externe avec Chromium headless.
|
||||
Réglages propres à Crawl4AI masqués avec le moteur intégré ; l'URL et
|
||||
la clé s'enregistrent en quittant le champ (onchange). -->
|
||||
<div class="rowbody">
|
||||
<div class="row" style="margin:0">
|
||||
<select id="web-engine" class="sctl" onchange="saveWebEngine()">
|
||||
<option value="go">Moteur Intégré</option>
|
||||
<option value="crawl4ai">Crawl4AI</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="crawl-fields">
|
||||
<div class="row">
|
||||
<input id="crawl-url" type="text" placeholder="http://localhost:11235" class="sctl" onchange="saveCrawlUrl()">
|
||||
</div>
|
||||
<!-- Clé d'accès du serveur Crawl4AI (facultative) : envoyée en
|
||||
« Authorization: Bearer … ». Jamais relue par l'API, on n'affiche
|
||||
qu'un indice sur les 4 derniers caractères. -->
|
||||
<div class="row">
|
||||
<input id="crawl-key" type="password" autocomplete="off" placeholder="clé API (si le serveur en exige une)" class="sctl" onchange="saveCrawlKey()">
|
||||
<button id="crawl-key-clear" onclick="clearCrawlKey()" style="display:none">retirer</button>
|
||||
</div>
|
||||
<div class="muted" style="font-size:11px;margin-top:4px">enregistré automatiquement en quittant le champ</div>
|
||||
<div id="crawl-key-state" class="muted" style="font-size:11px;margin-top:3px"></div>
|
||||
</div>
|
||||
<div id="internet-status" class="muted" style="font-size:11px;margin-top:4px"></div>
|
||||
</div>
|
||||
<!-- Clé d'accès du serveur Crawl4AI (facultative) : envoyée en
|
||||
« Authorization: Bearer … ». Jamais relue par l'API, on n'affiche
|
||||
qu'un indice sur les 4 derniers caractères. -->
|
||||
<div class="row" style="margin-top:6px">
|
||||
<input id="crawl-key" type="password" autocomplete="off" placeholder="clé API (si le serveur en exige une)" class="sctl" onchange="saveCrawlKey()">
|
||||
<button id="crawl-key-clear" onclick="clearCrawlKey()" style="display:none">retirer</button>
|
||||
</div>
|
||||
<div class="muted" style="font-size:11px;margin-top:4px">enregistré automatiquement en quittant le champ</div>
|
||||
<div id="crawl-key-state" class="muted" style="font-size:11px;margin-top:3px"></div>
|
||||
</div>
|
||||
<div id="internet-status" class="muted" style="font-size:11px;margin-top:4px"></div>
|
||||
</div>
|
||||
<div id="mcp-block" class="gated-block">
|
||||
<div id="mcp-block" class="gated-group">
|
||||
<div class="subhead">Serveurs MCP<span id="mcp-badge" class="sbadge"></span><span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Connecte des <b>serveurs MCP</b> (Model Context Protocol) pour donner de nouveaux outils à l'IA : accès fichiers, bases de données, APIs métier… <b>stdio</b> = programme lancé localement (npx/uvx/binaire) · <b>http</b> = serveur distant. ⚠ un serveur stdio exécute du code sur cette machine, au même titre que le shell.</span></span></div>
|
||||
<div id="mcp-list" class="mcp-list"></div>
|
||||
<button class="addbtn" style="margin-top:6px" onclick="openMcp('')">+ ajouter un serveur</button>
|
||||
<div class="slist">
|
||||
<div class="rowbody">
|
||||
<div id="mcp-list" class="mcp-list"></div>
|
||||
<button class="addbtn" style="margin-top:6px" onclick="openMcp('')">+ ajouter un serveur</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="param-block" class="gated-block">
|
||||
<div id="param-block" class="gated-group">
|
||||
<div class="subhead">Paramètres</div>
|
||||
<label class="switchrow" style="margin-top:8px"><span class="switch"><input type="checkbox" id="compact-toggle" onchange="toggleCompact()"><span class="slider"></span></span> compactage automatique du contexte<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">activé par défaut : quand la conversation approche de la limite de contexte, les vieux tours sont résumés automatiquement (façon Hermes) au lieu de t'imposer un « vider ». Discussions quasi illimitées sans perdre le fil.</span></span></label>
|
||||
<div class="slist">
|
||||
<label class="switchrow"><span class="switch"><input type="checkbox" id="compact-toggle" onchange="toggleCompact()"><span class="slider"></span></span> compactage automatique du contexte<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">activé par défaut : quand la conversation approche de la limite de contexte, les vieux tours sont résumés automatiquement (façon Hermes) au lieu de t'imposer un « vider ». Discussions quasi illimitées sans perdre le fil.</span></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<details><summary><span class="stitle">Apparence</span></summary>
|
||||
@@ -147,73 +165,82 @@
|
||||
</div>
|
||||
</details>
|
||||
<details><summary><span class="stitle">System prompt</span></summary>
|
||||
<textarea id="sysprompt" placeholder="(optionnel) ex: tu es un assistant concis en français" oninput="saveSys()" class="sctl"></textarea>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<textarea id="sysprompt" placeholder="(optionnel) ex: tu es un assistant concis en français" oninput="saveSys()" class="sctl"></textarea>
|
||||
</div></div>
|
||||
</details>
|
||||
<details><summary><span class="stitle">Accès OpenAI</span></summary>
|
||||
<div class="subhead">Endpoint compatible OpenAI<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Branchez n'importe quel client compatible OpenAI (OpenCode, Continue, Cursor…) sur ces adresses. Si une clé est définie, le client doit envoyer <code>Authorization: Bearer <clé></code>.</span></span></div>
|
||||
<div class="muted" style="font-size:11px;margin:0 0 10px">Modèle : <code>jean</code></div>
|
||||
|
||||
<div class="slist pad">
|
||||
<div class="io-cap">Adresse locale (réseau)</div>
|
||||
<div class="row">
|
||||
<input id="oai-url" readonly class="io-input">
|
||||
<button onclick="copyText(document.getElementById('oai-url').value,'adresse copiée')" title="copier">⧉</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="slist"><label class="switchrow"><span class="switch"><input type="checkbox" id="oai-public-toggle" onchange="toggleOAIPublic()"><span class="slider"></span></span> exposer en public (ajean.link)<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Rend votre IA joignable depuis <b>n'importe où</b> via <code>https://<machine>.oai.ajean.link/v1</code> (pour brancher un SaaS). Le trafic est chiffré <b>de bout en bout</b> jusqu'à ce serveur — le relais ajean.link ne voit rien. Nécessite l'accès distant actif.</span></span></label>
|
||||
</div>
|
||||
<div id="oai-public-wrap" class="slist pad" style="display:none">
|
||||
<div class="io-cap">Adresse publique — chiffrée de bout en bout, accessible partout</div>
|
||||
<div class="row">
|
||||
<input id="oai-public-url" readonly class="io-input">
|
||||
<button onclick="copyText(document.getElementById('oai-public-url').value,'adresse publique copiée')" title="copier">⧉</button>
|
||||
<!-- UNE carte pour tout l'endpoint : adresse locale, bascule « public » et
|
||||
adresse publique sont un seul sujet — trois cartes accolées donnaient
|
||||
trois îlots pour un même réglage. -->
|
||||
<div class="slist">
|
||||
<div class="rowbody">
|
||||
<div class="muted" style="margin:0 0 6px">Adresse locale (réseau) — modèle <code>jean</code></div>
|
||||
<div class="row" style="margin:0">
|
||||
<input id="oai-url" readonly class="io-input">
|
||||
<button class="iconbtn" onclick="copyText(document.getElementById('oai-url').value,'adresse copiée')" title="copier"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2.5"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
<label class="switchrow"><span class="switch"><input type="checkbox" id="oai-public-toggle" onchange="toggleOAIPublic()"><span class="slider"></span></span> exposer en public (ajean.link)<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Rend votre IA joignable depuis <b>n'importe où</b> via <code>https://<machine>.oai.ajean.link/v1</code> (pour brancher un SaaS). Le trafic est chiffré <b>de bout en bout</b> jusqu'à ce serveur — le relais ajean.link ne voit rien. Nécessite l'accès distant actif.</span></span></label>
|
||||
<div id="oai-public-wrap" class="rowbody" style="display:none">
|
||||
<div class="muted" style="margin:0 0 6px">Adresse publique — chiffrée de bout en bout, accessible partout</div>
|
||||
<div class="row" style="margin:0">
|
||||
<input id="oai-public-url" readonly class="io-input">
|
||||
<button class="iconbtn" onclick="copyText(document.getElementById('oai-public-url').value,'adresse publique copiée')" title="copier"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2.5"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="subhead">Clé API<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Protège l'endpoint des complétions (llama-server). Indispensable avant d'exposer le serveur sur internet. Modifier la clé <b>redémarre</b> le service.</span></span></div>
|
||||
<div class="slist pad">
|
||||
<div class="row">
|
||||
<input id="oai-key" readonly class="io-input">
|
||||
<button id="oai-key-eye" onclick="toggleKeyReveal()" title="afficher / masquer">afficher</button>
|
||||
<button onclick="copyApiKey()" title="copier la clé">⧉</button>
|
||||
</div>
|
||||
<div class="row" style="margin-top:6px">
|
||||
<button onclick="apiKeyAction('generate')">générer</button>
|
||||
<button onclick="apiKeySet()">définir…</button>
|
||||
<button onclick="apiKeyAction('clear')">retirer</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<div class="row" style="margin:0">
|
||||
<input id="oai-key" readonly class="io-input">
|
||||
<button id="oai-key-eye" onclick="toggleKeyReveal()" title="afficher / masquer">afficher</button>
|
||||
<button class="iconbtn" onclick="copyApiKey()" title="copier la clé"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2.5"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>
|
||||
</div>
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button onclick="apiKeyAction('generate')">générer</button>
|
||||
<button onclick="apiKeySet()">définir…</button>
|
||||
<button onclick="apiKeyAction('clear')">retirer</button>
|
||||
</div>
|
||||
</div></div>
|
||||
</details>
|
||||
<details id="remote-details"><summary><span class="stitle">Accès distant</span><span id="remote-badge" class="sbadge"></span></summary>
|
||||
<div class="subhead">ajean.link<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Accédez à ce serveur Jean depuis <b>n'importe où</b>, sans ouvrir de port, à travers le relais <b>ajean.link</b>. Le trafic est chiffré de <b>bout en bout</b> : le relais ne voit rien. Abonnement 4,80 €/mois.</span></span></div>
|
||||
<!-- Non connecté : un seul bouton, tout se passe dans la popup (compte + paiement). -->
|
||||
<div id="remote-off" class="slist pad">
|
||||
<p class="muted" style="font-size:12px;line-height:1.5;margin:6px 0 10px">Ouvrez l'accès distant à ce serveur en quelques clics, sans terminal.</p>
|
||||
<div id="remote-off" class="slist"><div class="rowbody">
|
||||
<p class="muted" style="margin:0 0 8px">Ouvrez l'accès distant à ce serveur en quelques clics, sans terminal.</p>
|
||||
<button onclick="remoteConnect()" style="border-color:var(--accent);color:var(--accent)">Connecter à ajean.link</button>
|
||||
</div>
|
||||
<!-- Connecté : URL publique + appairage + déconnexion. -->
|
||||
</div></div>
|
||||
<!-- Connecté : URL publique + appairage + déconnexion. Le bouton « démarrer
|
||||
le tunnel » vit DANS la carte de l'adresse : c'est l'action de cette
|
||||
ligne, il flottait auparavant entre deux cartes. -->
|
||||
<div id="remote-on" style="display:none">
|
||||
<div class="slist pad">
|
||||
<div class="io-cap">Votre adresse d'accès distant</div>
|
||||
<div class="row">
|
||||
<input id="remote-url" readonly class="io-input">
|
||||
<button onclick="copyText(document.getElementById('remote-url').value,'adresse copiée')" title="copier">⧉</button>
|
||||
<button onclick="window.open(document.getElementById('remote-url').value,'_blank')" title="ouvrir">↗</button>
|
||||
</div>
|
||||
<div id="remote-status" class="muted"></div>
|
||||
</div>
|
||||
<button id="remote-start" onclick="remoteStart()" style="display:none;margin-top:8px;border-color:var(--accent);color:var(--accent)">démarrer le tunnel</button>
|
||||
<div class="subhead" style="margin-top:12px">Première connexion<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">À votre première ouverture du portail distant, confirmez l'<b>empreinte</b> et saisissez le <b>code d'appairage</b> : cela verrouille la boîte noire sur CE serveur (anti-interception). Une seule fois par appareil.</span></span></div>
|
||||
<div class="slist pad">
|
||||
<div class="row"><button onclick="remotePairCode()">Afficher l'empreinte et le code d'appairage</button></div>
|
||||
<div id="remote-pair" class="muted" style="display:none"></div>
|
||||
<div class="row"><button onclick="remoteDisconnect()" class="btn-danger-o">déconnecter l'accès distant</button></div>
|
||||
</div>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<div class="muted" style="margin:0 0 6px">Votre adresse d'accès distant</div>
|
||||
<div class="row" style="margin:0">
|
||||
<input id="remote-url" readonly class="io-input">
|
||||
<button class="iconbtn" onclick="copyText(document.getElementById('remote-url').value,'adresse copiée')" title="copier"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2.5"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>
|
||||
<button class="iconbtn" onclick="window.open(document.getElementById('remote-url').value,'_blank')" title="ouvrir"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><path d="M15 3h6v6"/><path d="M10 14L21 3"/></svg></button>
|
||||
</div>
|
||||
<div id="remote-status" class="muted" style="margin-top:6px"></div>
|
||||
<div class="row" style="margin-bottom:0"><button id="remote-start" onclick="remoteStart()" style="display:none;border-color:var(--accent);color:var(--accent)">démarrer le tunnel</button></div>
|
||||
</div></div>
|
||||
<div class="subhead">Première connexion<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">À votre première ouverture du portail distant, confirmez l'<b>empreinte</b> et saisissez le <b>code d'appairage</b> : cela verrouille la boîte noire sur CE serveur (anti-interception). Une seule fois par appareil.</span></span></div>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<div class="row" style="margin:0"><button onclick="remotePairCode()">Afficher l'empreinte et le code d'appairage</button></div>
|
||||
<div id="remote-pair" class="muted" style="display:none;margin-top:6px"></div>
|
||||
<div class="row" style="margin-bottom:0"><button onclick="remoteDisconnect()" class="btn-danger-o">déconnecter l'accès distant</button></div>
|
||||
</div></div>
|
||||
</div>
|
||||
</details>
|
||||
<details id="lc-details"><summary><span class="stitle">Moteur</span></summary>
|
||||
<div id="lc-status" class="lc-head muted">…</div>
|
||||
<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">
|
||||
<div class="rowbody"><div id="lc-status" class="lc-head muted">…</div></div>
|
||||
<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>
|
||||
@@ -231,33 +258,36 @@
|
||||
<div class="lc-mode-state" id="lc-custom-state"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="lc-job" style="display:none;margin-top:10px">
|
||||
<div id="lc-job" class="rowbody" style="display:none">
|
||||
<div id="lc-job-phase" style="font-size:12px;display:flex;align-items:center;gap:6px"></div>
|
||||
<div class="lc-logbar" onclick="lcToggleLog()"><span id="lc-log-caret">▸</span> détails</div>
|
||||
<pre id="lc-log" hidden></pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Ces trois boutons pilotent le service qui fait tourner llama-server :
|
||||
leur place est ici, avec le moteur, et pas dans une section « Service »
|
||||
à part qui ne contenait qu'eux. -->
|
||||
<div class="subhead">Service du moteur<span class="help" tabindex="0" onclick="event.preventDefault();event.stopPropagation();this.classList.toggle('open')">?<span class="tip">Démarre, redémarre ou arrête <b>llama-server</b> — le processus qui charge le modèle et répond. Un redémarrage recharge le modèle du preset actif (quelques secondes à quelques minutes selon sa taille).</span></span></div>
|
||||
<div class="slist pad">
|
||||
<div class="row">
|
||||
<div class="slist"><div class="rowbody">
|
||||
<div class="row" style="margin:0">
|
||||
<button onclick="act('start')">démarrer</button>
|
||||
<button onclick="act('restart')">redémarrer</button>
|
||||
<button onclick="act('stop')">arrêter</button>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
</details>
|
||||
<details id="upd-details"><summary><span class="stitle">Actions</span></summary>
|
||||
<div class="slist pad">
|
||||
<div class="row"><button id="upd-check" onclick="checkUpdate()">↥ Vérifier les mises à jour</button></div>
|
||||
<div class="slist"><div class="rowbody">
|
||||
<div class="row" style="margin:0">
|
||||
<button id="upd-check" onclick="checkUpdate()">↥ Vérifier les mises à jour</button>
|
||||
</div>
|
||||
<div id="upd-msg" class="muted"></div>
|
||||
<div class="row">
|
||||
<div class="row" style="margin-bottom:0">
|
||||
<button onclick="loadAll()">↻ refresh</button>
|
||||
<button onclick="resetChat()">⌫ clear chat</button>
|
||||
<button id="btn-bench" onclick="runBenchUI()">bench</button>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>
|
||||
</details>
|
||||
</div>
|
||||
<div class="main" style="position:relative">
|
||||
@@ -265,6 +295,16 @@
|
||||
<!-- Voile de chargement du fil : sans lui, une connexion lente donne un chat
|
||||
VIDE indiscernable d'une conversation vide. Masqué au signal {caught_up}. -->
|
||||
<div id="chat-loading"><span class="cl-dots"><span></span><span></span><span></span></span><span id="chat-loading-text">chargement de la conversation…</span></div>
|
||||
<!-- Accueil : affiché UNIQUEMENT quand le fil est réellement vide (et une fois
|
||||
le replay terminé, sinon il clignoterait au chargement de chaque page).
|
||||
Le logo est cloné depuis #brand au démarrage — une seule source de vérité
|
||||
pour le dessin des lettres. -->
|
||||
<div id="chat-empty" aria-hidden="true">
|
||||
<div id="ce-logo"></div>
|
||||
<!-- Une seule ligne, NEUTRE en genre : l'UI est utilisée par tout le monde,
|
||||
pas de civilité ni d'accord qui obligerait à deviner. -->
|
||||
<div id="ce-title">On commence par quoi ?</div>
|
||||
</div>
|
||||
<button id="scrollbtn" onclick="jumpBottom()" title="aller en bas">↓</button>
|
||||
<div id="composer">
|
||||
<div id="ctxmeta">
|
||||
@@ -302,6 +342,10 @@
|
||||
<div id="ask-title" class="ask-title"></div>
|
||||
<div id="ask-msg" class="ask-msg"></div>
|
||||
<input id="ask-input" class="ask-input" style="display:none">
|
||||
<!-- Option facultative de la confirmation (ex. « supprimer aussi le .gguf ») :
|
||||
sa place est ICI, dans la boîte qui pose la question, plutôt que dans le
|
||||
pied de l'éditeur où elle occupait de la place en permanence. -->
|
||||
<label id="ask-check" class="pe-delmodel" style="display:none"><input type="checkbox" id="ask-check-input"><span id="ask-check-label"></span></label>
|
||||
<div class="ask-actions">
|
||||
<button id="ask-cancel" onclick="askResolve(false)">Annuler</button>
|
||||
<button id="ask-ok" class="ask-ok" onclick="askResolve(true)">Confirmer</button>
|
||||
@@ -403,6 +447,11 @@
|
||||
<strong id="modal-title">Preset</strong>
|
||||
<button class="modal-x" onclick="closeModal()" aria-label="Fermer">×</button>
|
||||
</div>
|
||||
<!-- Chargement : la modale s'ouvre tout de suite, mais son contenu n'arrive
|
||||
qu'après (jusqu'à 3 requêtes en accès distant). Ce voile se pose PAR-DESSUS
|
||||
le formulaire, qui reste en place (invisible) : la carte garde sa taille
|
||||
finale, donc pas de saut au moment où le contenu apparaît. -->
|
||||
<div class="modal-load"><span class="spinner"></span></div>
|
||||
<div class="pe-body">
|
||||
<div class="pe-group">
|
||||
<div class="pe-gh">Nom</div>
|
||||
@@ -583,10 +632,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Pied compact, sur UNE ligne : la suppression est une icône (action rare et
|
||||
destructrice, elle n'a pas à peser autant que « enregistrer »), et sa case
|
||||
« supprimer aussi le .gguf » se réduit à l'extension, le libellé complet
|
||||
restant en infobulle. -->
|
||||
<div class="pe-foot">
|
||||
<div class="foot-l">
|
||||
<button id="m-del" class="pe-btn ghost-danger" onclick="delItem()">supprimer</button>
|
||||
<label id="m-del-model-wrap" class="pe-delmodel" style="display:none"><input type="checkbox" id="m-del-model"> + le modèle .gguf</label>
|
||||
<button id="m-del" class="pe-del" onclick="delItem()" title="supprimer" aria-label="supprimer"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 011-1h6a1 1 0 011 1v2"/><path d="M19 6l-1 14a2 2 0 01-2 2H8a2 2 0 01-2-2L5 6"/><path d="M10 11v6"/><path d="M14 11v6"/></svg></button>
|
||||
</div>
|
||||
<div class="foot-r">
|
||||
<button class="pe-btn" onclick="closeModal()">annuler</button>
|
||||
|
||||
Reference in new issue
Block a user