CI / lint (push) Successful in 1m12s
CI / security (push) Successful in 43s
CI / test (push) Successful in 1m10s
CI / build (push) Successful in 35s
CI / e2e (push) Successful in 11m47s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Contexte: - Repertoire (mode historique BooksLM, via menu contextuel dossier) - Documents (bouton flottant quand des documents sont ouverts) - General (bouton flottant sans document: aide app + creation de fichiers) - Le contexte est affiche dans le header; suggestions adaptees au mode - Blocs obsigate-action rendus en carte avec bouton Appliquer (pas d'execution auto) Backend: - /api/ai/bookslm/context et /chat acceptent mode + context_files - collect_files_context(), empty_context(), build_general_system_prompt() - mode documents degrade vers general si aucun fichier lisible Corrige: - bouton flottant passait le chemin d'un fichier comme repertoire -> erreur 'Aucun fichier markdown trouve dans ce dossier' - panneau precedent masque (localStorage) ne se rouvrait plus - selecteur fournisseur/modele deplace sur une ligne dediee (header actions visibles) - Entree envoie, Ctrl+Entree insere un saut de ligne Tests: test_bookslm.py (+13) et tests/frontend/ai.test.mjs (+8)
57 lines
1.8 KiB
JavaScript
57 lines
1.8 KiB
JavaScript
// ObsiGate — Floating Action Button (FAB) for the AI Assistant sidebar.
|
|
// Always visible in the bottom-right corner; hides while the assistant panel
|
|
// is expanded. Clicking it opens the assistant in the context that matches
|
|
// the current application state (open documents, or General when none).
|
|
import booksLM from './bookslm.js';
|
|
import { t } from './i18n.js';
|
|
import { safeCreateIcons } from './utils.js';
|
|
|
|
let _fab = null;
|
|
|
|
// The panel is visible when it has `.open` without `.hidden`.
|
|
function isBooksLmVisible() {
|
|
const panel = document.querySelector('.bookslm-panel');
|
|
return !!(panel && panel.classList.contains('open') && !panel.classList.contains('hidden'));
|
|
}
|
|
|
|
function syncVisibility() {
|
|
if (!_fab) return;
|
|
const visible = isBooksLmVisible();
|
|
_fab.hidden = visible;
|
|
const label = visible ? t('fab.close') : t('fab.open');
|
|
_fab.title = label;
|
|
_fab.setAttribute('aria-label', label);
|
|
}
|
|
|
|
async function onClick() {
|
|
const panel = document.querySelector('.bookslm-panel');
|
|
// Collapsed panel: reveal it instead of reopening.
|
|
if (panel && panel.classList.contains('open') && panel.classList.contains('hidden')) {
|
|
booksLM._toggleSidebar();
|
|
return;
|
|
}
|
|
// Otherwise open (or re-focus) the assistant for the current context.
|
|
await booksLM.openForCurrentContext();
|
|
}
|
|
|
|
export function initAIFab() {
|
|
if (_fab) return;
|
|
|
|
const btn = document.createElement('button');
|
|
btn.type = 'button';
|
|
btn.id = 'ai-fab';
|
|
btn.className = 'ai-fab';
|
|
btn.innerHTML = '<i data-lucide="bot" style="width:24px;height:24px"></i>';
|
|
document.body.appendChild(btn);
|
|
_fab = btn;
|
|
|
|
btn.addEventListener('click', onClick);
|
|
document.addEventListener('bookslm:opened', syncVisibility);
|
|
document.addEventListener('bookslm:closed', syncVisibility);
|
|
|
|
if (typeof safeCreateIcons === 'function') safeCreateIcons();
|
|
syncVisibility();
|
|
}
|
|
|
|
export default initAIFab;
|