feat(ui): badge version visible + FAB AI flottant
CI / lint (push) Successful in 37s
CI / security (push) Successful in 25s
CI / test (push) Successful in 48s
CI / build (push) Successful in 21s
CI / e2e (push) Successful in 6m4s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Successful in 37s
CI / security (push) Successful in 25s
CI / test (push) Successful in 48s
CI / build (push) Successful in 21s
CI / e2e (push) Successful in 6m4s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
// ObsiGate — Floating Action Button (FAB) to open/close the BooksLM AI sidebar.
|
||||
// Always visible in the bottom-right corner; hides while BooksLM is open.
|
||||
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`).
|
||||
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 f = getCurrentFile();
|
||||
if (!f) {
|
||||
showToast(t('toast.no_open_file'), 'error');
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
Reference in New Issue
Block a user