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:
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user