feat(bookslm): bouton toggle cacher/afficher la sidebar dans le header
CI / lint (push) Successful in 37s
CI / security (push) Successful in 24s
CI / test (push) Successful in 49s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m16s
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 24s
CI / test (push) Successful in 49s
CI / build (push) Successful in 22s
CI / e2e (push) Successful in 6m16s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Même mécanisme que le sidebar de navigation gauche : - Bouton .bookslm-btn-toggle dans le header (icône panel-right-close/open) - Toggle la classe .hidden sur le panel, persiste dans localStorage - État restauré au prochain open() - Le panel reste monté (pas de teardown), juste slid off-screen - CSS : .bookslm-panel.open.hidden → translateX(100%) - CSS : .bookslm-btn-toggle suit le pattern .sidebar-toggle-btn i18n : bookslm.toggle_sidebar ajouté (fr + en) Import safeCreateIcons ajouté pour rafraîchir l'icône Lucide au toggle. Vérifié : - pytest : 504 passed (zéro régression) - ruff : All checks passed - frontend unit : 7 passed - validate-imports : 30 modules / 204 exports - pane-manager JSDOM : 9/9
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// BooksLM — Directory-scoped AI chat panel (style NotebookLM)
|
||||
import { t } from './i18n.js';
|
||||
import { buildAIPickerUI } from './ai.js';
|
||||
import { safeCreateIcons } from './utils.js';
|
||||
|
||||
class BooksLM {
|
||||
constructor() {
|
||||
@@ -43,6 +44,14 @@ class BooksLM {
|
||||
// Open with animation
|
||||
requestAnimationFrame(() => {
|
||||
this._panel.classList.add('open');
|
||||
// Apply persisted hidden state (user toggled off in a previous session)
|
||||
try {
|
||||
const savedHidden = localStorage.getItem('obsigate-bookslm-hidden');
|
||||
if (savedHidden === 'true') {
|
||||
this._panel.classList.add('hidden');
|
||||
this._updateToggleIcon();
|
||||
}
|
||||
} catch { /* */ }
|
||||
});
|
||||
this._isOpen = true;
|
||||
|
||||
@@ -77,6 +86,39 @@ class BooksLM {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the BooksLM sidebar visibility (collapsed/hidden to the right edge
|
||||
* while the panel stays mounted). The state is persisted in localStorage so
|
||||
* the user choice survives page reloads — same UX as the left sidebar toggle.
|
||||
*
|
||||
* The panel stays in the DOM (no teardown); only a CSS transform is applied,
|
||||
* which is what makes this fast and matches the right-sidebar pattern in
|
||||
* ObsiGate.
|
||||
*/
|
||||
_toggleSidebar() {
|
||||
if (!this._panel) return;
|
||||
const isHidden = this._panel.classList.toggle('hidden');
|
||||
try {
|
||||
localStorage.setItem('obsigate-bookslm-hidden', isHidden ? 'true' : 'false');
|
||||
} catch { /* */ }
|
||||
this._updateToggleIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the toggle icon between "panel-right-close" (visible) and
|
||||
* "panel-right-open" (hidden) so the button visually reflects the state.
|
||||
*/
|
||||
_updateToggleIcon() {
|
||||
if (!this._panel) return;
|
||||
const btn = this._panel.querySelector('.bookslm-btn-toggle');
|
||||
if (!btn) return;
|
||||
const isHidden = this._panel.classList.contains('hidden');
|
||||
const icon = btn.querySelector('i[data-lucide]');
|
||||
if (!icon) return;
|
||||
icon.setAttribute('data-lucide', isHidden ? 'panel-right-open' : 'panel-right-close');
|
||||
if (typeof safeCreateIcons === 'function') safeCreateIcons();
|
||||
}
|
||||
|
||||
newConversation() {
|
||||
this._messages = [];
|
||||
this._saveHistory();
|
||||
@@ -117,6 +159,9 @@ class BooksLM {
|
||||
panel.className = 'bookslm-panel';
|
||||
panel.innerHTML = `
|
||||
<div class="bookslm-header">
|
||||
<button class="bookslm-btn-toggle" title="${t('bookslm.toggle_sidebar')}" aria-label="${t('bookslm.toggle_sidebar')}">
|
||||
<i data-lucide="panel-right-close" style="width:16px;height:16px"></i>
|
||||
</button>
|
||||
<span>📚</span>
|
||||
<span class="bookslm-title"></span>
|
||||
<span class="bookslm-picker-host" style="margin-left:auto"></span>
|
||||
@@ -144,6 +189,7 @@ class BooksLM {
|
||||
panel.querySelector('.bookslm-btn-close').addEventListener('click', () => this.close());
|
||||
panel.querySelector('.bookslm-btn-new').addEventListener('click', () => this.newConversation());
|
||||
panel.querySelector('.bookslm-btn-export').addEventListener('click', () => this.exportConversation());
|
||||
panel.querySelector('.bookslm-btn-toggle').addEventListener('click', () => this._toggleSidebar());
|
||||
panel.querySelector('.bookslm-btn-fullscreen').addEventListener('click', () => {
|
||||
this._isFullscreen = !this._isFullscreen;
|
||||
panel.classList.toggle('fullscreen', this._isFullscreen);
|
||||
|
||||
@@ -1589,6 +1589,7 @@
|
||||
"bookslm.send": "Send",
|
||||
"bookslm.new_conversation": "New conversation",
|
||||
"bookslm.export": "Export conversation",
|
||||
"bookslm.toggle_sidebar": "Hide/Show AI sidebar",
|
||||
"bookslm.copy": "Copy",
|
||||
"bookslm.regenerate": "Regenerate",
|
||||
"bookslm.suggestion_summary": "Summarize this directory",
|
||||
|
||||
@@ -1589,6 +1589,7 @@
|
||||
"bookslm.send": "Envoyer",
|
||||
"bookslm.new_conversation": "Nouvelle conversation",
|
||||
"bookslm.export": "Exporter la conversation",
|
||||
"bookslm.toggle_sidebar": "Masquer/Afficher la sidebar AI",
|
||||
"bookslm.copy": "Copier",
|
||||
"bookslm.regenerate": "Régénérer",
|
||||
"bookslm.suggestion_summary": "Résume ce répertoire",
|
||||
|
||||
@@ -9021,6 +9021,19 @@ body.popup-mode .content-area {
|
||||
box-shadow: -4px 0 20px rgba(0,0,0,0.3);
|
||||
color: var(--text-primary); }
|
||||
.bookslm-panel.open { transform: translateX(0); }
|
||||
/* The `.hidden` class is applied by the header toggle button to collapse the
|
||||
panel while keeping it mounted (so the toggle icon stays usable). When
|
||||
hidden, the panel is slid fully off-screen and the open animation no
|
||||
longer overrides the translateX. */
|
||||
.bookslm-panel.open.hidden { transform: translateX(100%); }
|
||||
.bookslm-btn-toggle {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
background: none; border: none; cursor: pointer;
|
||||
color: var(--text-secondary); padding: 4px;
|
||||
border-radius: 4px; flex-shrink: 0;
|
||||
transition: color 200ms ease, background 200ms ease;
|
||||
}
|
||||
.bookslm-btn-toggle:hover { color: var(--accent); background: var(--surface2); }
|
||||
.bookslm-header { display: flex; align-items: center; gap: 8px;
|
||||
padding: 12px 16px; border-bottom: 1px solid var(--border);
|
||||
font-size: 14px; font-weight: 600; }
|
||||
|
||||
Reference in New Issue
Block a user