feat(ai): assistant AI multi-contexte + ergonomie sidebar et clavier
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)
This commit is contained in:
2026-09-11 09:11:00 -04:00
parent 62023acd4d
commit d6fdcef4d4
14 changed files with 1049 additions and 214 deletions
+11 -34
View File
@@ -1,26 +1,14 @@
// ObsiGate — Floating Action Button (FAB) to open/close the BooksLM AI sidebar.
// Always visible in the bottom-right corner; hides while BooksLM is open.
// 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 { state } from './state.js';
import { showToast, TabManager } from './ui.js';
import { t } from './i18n.js';
import { safeCreateIcons } from './utils.js';
let _fab = null;
// Mirror palette.js getCurrentFile(): prefer the active tab, then fall back to state.
function getCurrentFile() {
const id = TabManager._activeTabId;
if (id) {
const tab = TabManager._tabs.find((x) => x.id === id);
if (tab) return { vault: tab.vault, path: tab.path };
}
if (state.currentVault && state.currentPath) return { vault: state.currentVault, path: state.currentPath };
return null;
}
// The BooksLM panel is considered visible when it has `.open` without `.hidden`
// (the header toggle collapses it off-screen via `.hidden`).
// 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'));
@@ -36,25 +24,14 @@ function syncVisibility() {
}
async function onClick() {
const f = getCurrentFile();
if (!f) {
showToast(t('toast.no_open_file'), 'error');
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;
}
const sameDir = booksLM._isOpen && booksLM._vault === f.vault && booksLM._directory === f.path;
if (sameDir) {
// Already open for this directory: re-show a collapsed panel, or close it.
const panel = document.querySelector('.bookslm-panel');
if (panel && panel.classList.contains('hidden')) {
booksLM._toggleSidebar();
} else {
booksLM.close();
}
return;
}
await booksLM.open(f.vault, f.path);
// Otherwise open (or re-focus) the assistant for the current context.
await booksLM.openForCurrentContext();
}
export function initAIFab() {