Coverage for src/lilbee/cli/tui/widgets/sessions_drawer.py: 100%
28 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-28 17:20 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-09-28 17:20 +0000
1"""Sessions side drawer: the session list docked left so chat stays live.
3Non-modal, mirroring the Fleet drawer. Docked left so the screen underneath
4reflows to the right and the chat prompt keeps working while the drawer is open.
5The drawer closes when the embedded panel opens a chat or asks to close.
6"""
8from __future__ import annotations
10from pathlib import Path
11from typing import ClassVar
13from textual import on
14from textual.app import ComposeResult
15from textual.message import Message
16from textual.screen import Screen
18from lilbee.cli.tui.widgets.drawer import Drawer
19from lilbee.cli.tui.widgets.session_list import SessionListPanel
21_DRAWER_CSS = (Path(__file__).parent / "sessions_drawer.tcss").read_text(encoding="utf-8")
23# CSS class set on the host screen so app.tcss insets the docked bars to the
24# space right of the drawer.
25_HOST_OPEN_CLASS = "sessions-open"
28class SessionsDrawer(Drawer):
29 """Session switcher as a non-modal left-side drawer; closed with esc or ctrl+o."""
31 DEFAULT_CSS: ClassVar[str] = _DRAWER_CSS
33 def __init__(self) -> None:
34 super().__init__(id="sessions-drawer")
35 self._host: Screen[object] | None = None
37 def compose(self) -> ComposeResult:
38 yield SessionListPanel()
40 def on_mount(self) -> None:
41 """Inset the host screen's docked bars so the drawer sits beside them."""
42 self._host = self.screen
43 self._host.add_class(_HOST_OPEN_CLASS)
45 def on_unmount(self) -> None:
46 """Restore the bars to full width when the drawer closes by any path."""
47 if self._host is not None:
48 self._host.remove_class(_HOST_OPEN_CLASS)
50 @on(SessionListPanel.ChatOpened)
51 @on(SessionListPanel.CloseRequested)
52 def _on_leave(self, _event: Message) -> None:
53 self.remove()