Complete Phase 1: parallel sync, IPC, theme colors, lazy CLI loading

- Sync: Parallelize message downloads with asyncio.gather (batch size 5)
- Sync: Increase HTTP semaphore from 2 to 5 concurrent requests
- Sync: Add IPC notifications to sync daemon after sync completes
- Mail: Replace all hardcoded RGB colors with theme variables
- Mail: Remove envelope icon/checkbox gap (padding cleanup)
- Mail: Add IPC listener for refresh notifications from sync
- Calendar: Style current time line with error color and solid line
- Tasks: Fix table not displaying (CSS grid to horizontal layout)
- CLI: Implement lazy command loading for faster startup (~12s to ~0.3s)
- Add PROJECT_PLAN.md with full improvement roadmap
- Add src/utils/ipc.py for Unix socket cross-app communication
This commit is contained in:
Bendt
2025-12-19 10:29:53 -05:00
parent a41d59e529
commit d4226caf0a
11 changed files with 1096 additions and 103 deletions

View File

@@ -10,6 +10,7 @@ from .actions.delete import delete_current
from src.services.taskwarrior import client as taskwarrior_client
from src.services.himalaya import client as himalaya_client
from src.utils.shared_config import get_theme_name
from src.utils.ipc import IPCListener, IPCMessage
from textual.containers import Container, ScrollableContainer, Vertical, Horizontal
from textual.timer import Timer
from textual.binding import Binding
@@ -149,7 +150,7 @@ class EmailViewerApp(App):
async def on_mount(self) -> None:
self.alert_timer: Timer | None = None # Timer to throttle alerts
self.theme = get_theme_name()
self.title = "MaildirGTD"
self.title = "LUK Mail"
self.query_one("#main_content").border_title = self.status_title
sort_indicator = "" if self.sort_order_ascending else ""
self.query_one("#envelopes_list").border_title = f"1⃣ Emails {sort_indicator}"
@@ -157,6 +158,10 @@ class EmailViewerApp(App):
self.query_one("#folders_list").border_title = "3⃣ Folders"
# Start IPC listener for refresh notifications from sync daemon
self._ipc_listener = IPCListener("mail", self._on_ipc_message)
self._ipc_listener.start()
self.fetch_accounts()
self.fetch_folders()
worker = self.fetch_envelopes()
@@ -164,6 +169,12 @@ class EmailViewerApp(App):
self.query_one("#envelopes_list").focus()
self.action_oldest()
def _on_ipc_message(self, message: IPCMessage) -> None:
"""Handle IPC messages from sync daemon."""
if message.event == "refresh":
# Schedule a reload on the main thread
self.call_from_thread(self.fetch_envelopes)
def compute_status_title(self):
metadata = self.message_store.get_metadata(self.current_message_id)
message_date = metadata["date"] if metadata else "N/A"
@@ -820,6 +831,9 @@ class EmailViewerApp(App):
self.query_one("#envelopes_list").focus()
def action_quit(self) -> None:
# Stop IPC listener before exiting
if hasattr(self, "_ipc_listener"):
self._ipc_listener.stop()
self.exit()
def action_toggle_selection(self) -> None:

View File

@@ -3,7 +3,7 @@
#main_content, .list_view {
scrollbar-size: 1 1;
border: round rgb(117, 106, 129);
border: round $border;
height: 1fr;
}
@@ -43,18 +43,18 @@
#main_content:focus, .list_view:focus {
border: round $secondary;
background: rgb(55, 53, 57);
background: $surface;
border-title-style: bold;
}
Label#task_prompt {
padding: 1;
color: rgb(128,128,128);
color: $text-muted;
}
Label#task_prompt_label {
padding: 1;
color: rgb(255, 216, 102);
color: $warning;
}
Label#message_label {
@@ -66,7 +66,7 @@ StatusTitle {
width: 100%;
height: 1;
color: $text;
background: rgb(64, 62, 65);
background: $panel;
content-align: center middle;
}
@@ -113,8 +113,8 @@ EnvelopeListItem .envelope-row-3 {
}
EnvelopeListItem .status-icon {
width: 3;
padding: 0 1 0 0;
width: 2;
padding: 0;
color: $text-muted;
}
@@ -124,7 +124,7 @@ EnvelopeListItem .status-icon.unread {
EnvelopeListItem .checkbox {
width: 2;
padding: 0 1 0 0;
padding: 0;
}
EnvelopeListItem .sender-name {
@@ -166,11 +166,11 @@ EnvelopeListItem.selected {
GroupHeader {
height: 1;
width: 1fr;
background: rgb(64, 62, 65);
background: $panel;
}
GroupHeader .group-header-label {
color: rgb(160, 160, 160);
color: $text-muted;
text-style: bold;
padding: 0 1;
width: 1fr;
@@ -222,10 +222,10 @@ GroupHeader .group-header-label {
#envelopes_list {
ListItem:odd {
background: rgb(45, 45, 46);
background: $surface;
}
ListItem:even {
background: rgb(50, 50, 56);
background: $surface-darken-1;
}
& > ListItem {
@@ -269,9 +269,9 @@ GroupHeader .group-header-label {
}
Label.group_header {
color: rgb(140, 140, 140);
color: $text-muted;
text-style: bold;
background: rgb(64, 62, 65);
background: $panel;
width: 100%;
padding: 0 1;
}
@@ -300,6 +300,3 @@ ContentContainer {
width: 100%;
height: 1fr;
}
.checkbox {
padding-right: 1;
}

View File

@@ -44,12 +44,17 @@ class EnvelopeListItem(Static):
EnvelopeListItem .status-icon {
width: 2;
padding: 0 1 0 0;
padding: 0;
}
EnvelopeListItem .checkbox {
width: 2;
padding: 0 1 0 0;
padding: 0;
}
EnvelopeListItem .checkbox {
width: 2;
padding: 0;
}
EnvelopeListItem .sender-name {