Add context filter to Tasks TUI and fix calendar UI bugs
Tasks TUI: - Add context support to TaskBackend interface (get_context, set_context, get_contexts methods) - Implement context methods in DstaskClient - Add Context section to FilterSidebar (above projects/tags) - Context changes persist via backend CLI Calendar TUI: - Remove duplicate header from InvitesPanel (use border_title instead) - Fix border_title color to use $primary - Fix WeekGrid to always scroll to work day start (7am) on mount
This commit is contained in:
@@ -39,6 +39,7 @@ class InvitesPanel(Widget):
|
||||
min-height: 3;
|
||||
padding: 0 1;
|
||||
border: round $primary;
|
||||
border-title-color: $primary;
|
||||
}
|
||||
"""
|
||||
|
||||
@@ -74,7 +75,12 @@ class InvitesPanel(Widget):
|
||||
|
||||
def on_mount(self) -> None:
|
||||
"""Set border title on mount."""
|
||||
self.border_title = "Invites"
|
||||
self._update_border_title()
|
||||
|
||||
def _update_border_title(self) -> None:
|
||||
"""Update border title with invite count."""
|
||||
count = len(self.invites)
|
||||
self.border_title = f"Invites ({count})" if count else "Invites"
|
||||
|
||||
def _get_theme_color(self, color_name: str) -> str:
|
||||
"""Get a color from the current theme."""
|
||||
@@ -97,25 +103,21 @@ class InvitesPanel(Widget):
|
||||
return fallbacks.get(color_name, "white")
|
||||
|
||||
def get_content_height(self, container, viewport, width: int) -> int:
|
||||
"""Calculate height: header + invite rows."""
|
||||
# Header (1) + invites (2 lines each) + empty message if no invites
|
||||
"""Calculate height: invite rows only (no internal header)."""
|
||||
if not self.invites:
|
||||
return 2 # Header + "No pending invites"
|
||||
return 1 + len(self.invites) * 2 # Header + 2 lines per invite
|
||||
return 1 # "No pending invites"
|
||||
return len(self.invites) * 2 # 2 lines per invite
|
||||
|
||||
def render_line(self, y: int) -> Strip:
|
||||
"""Render a line of the panel."""
|
||||
if y == 0:
|
||||
return self._render_header()
|
||||
|
||||
if not self.invites:
|
||||
if y == 1:
|
||||
if y == 0:
|
||||
return self._render_empty_message()
|
||||
return Strip.blank(self.size.width)
|
||||
|
||||
# Each invite takes 2 lines
|
||||
invite_idx = (y - 1) // 2
|
||||
line_in_invite = (y - 1) % 2
|
||||
invite_idx = y // 2
|
||||
line_in_invite = y % 2
|
||||
|
||||
if 0 <= invite_idx < len(self.invites):
|
||||
return self._render_invite_line(
|
||||
@@ -126,15 +128,6 @@ class InvitesPanel(Widget):
|
||||
|
||||
return Strip.blank(self.size.width)
|
||||
|
||||
def _render_header(self) -> Strip:
|
||||
"""Render the panel header."""
|
||||
primary_color = self._get_theme_color("primary")
|
||||
count = len(self.invites)
|
||||
header = f"Invites ({count})" if count else "Invites"
|
||||
header = header[: self.size.width].ljust(self.size.width)
|
||||
style = Style(bold=True, color=primary_color)
|
||||
return Strip([Segment(header, style)])
|
||||
|
||||
def _render_empty_message(self) -> Strip:
|
||||
"""Render empty state message."""
|
||||
msg = "No pending invites"
|
||||
@@ -179,6 +172,7 @@ class InvitesPanel(Widget):
|
||||
self.invites = invites
|
||||
if self.selected_index >= len(invites):
|
||||
self.selected_index = max(0, len(invites) - 1)
|
||||
self._update_border_title()
|
||||
self.refresh()
|
||||
|
||||
def select_next(self) -> None:
|
||||
@@ -203,11 +197,11 @@ class InvitesPanel(Widget):
|
||||
"""Handle mouse clicks."""
|
||||
y = event.y
|
||||
|
||||
if y == 0 or not self.invites:
|
||||
if not self.invites:
|
||||
return
|
||||
|
||||
# Calculate which invite was clicked
|
||||
invite_idx = (y - 1) // 2
|
||||
# Calculate which invite was clicked (2 lines per invite)
|
||||
invite_idx = y // 2
|
||||
if 0 <= invite_idx < len(self.invites):
|
||||
self.selected_index = invite_idx
|
||||
self.post_message(self.InviteSelected(self.invites[invite_idx]))
|
||||
|
||||
Reference in New Issue
Block a user