Files
ObsiGate/frontend/js/ai-fab.js
T
bruno d6fdcef4d4
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
feat(ai): assistant AI multi-contexte + ergonomie sidebar et clavier
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)
2026-09-11 09:11:00 -04:00

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;