From 7a5b91141408d97436af40b006bca74bcaa64c1f Mon Sep 17 00:00:00 2001 From: Tim Bendt Date: Wed, 30 Apr 2025 16:47:16 -0400 Subject: [PATCH] basic nav status counts --- .../__pycache__/archive.cpython-311.pyc | Bin 1840 -> 1751 bytes .../actions/__pycache__/next.cpython-311.pyc | Bin 2836 -> 2899 bytes .../actions/__pycache__/open.cpython-311.pyc | Bin 1376 -> 1274 bytes .../__pycache__/previous.cpython-311.pyc | Bin 2048 -> 1975 bytes .../__pycache__/show_message.cpython-311.pyc | Bin 1770 -> 1821 bytes maildir_gtd/actions/archive.py | 1 - maildir_gtd/actions/next.py | 10 +-- maildir_gtd/actions/open.py | 1 - maildir_gtd/actions/previous.py | 3 +- maildir_gtd/actions/show_message.py | 3 +- maildir_gtd/app.py | 62 ++++++++++++++++-- maildir_gtd/email_viewer.tcss | 8 +++ maildir_gtd/screens/CreateTask.py | 3 +- .../__pycache__/CreateTask.cpython-311.pyc | Bin 2181 -> 2095 bytes 14 files changed, 72 insertions(+), 19 deletions(-) diff --git a/maildir_gtd/actions/__pycache__/archive.cpython-311.pyc b/maildir_gtd/actions/__pycache__/archive.cpython-311.pyc index 65e3b40534060cd9b3761c964a99d981b69cd4fa..1ad86694f9ebe55fa8d73d6d0c9db4a866ac6da3 100644 GIT binary patch delta 190 zcmdnMcb%7aIWI340}v>*31#F=IztV^f{6=7CT^L*m^Rs$F^?I--#m?R zA(Mu?erR!OQL%nWW^PhyUP_6+OKMqaPJTgZk$!$@Nlt!#wr)wjZf5f2WELyN%*pdu zG#Gg&A7`mlyuriY@89KrLB?}I@fCGjAPqqmdAzRhcwOM}n(V;3UdBn**@EGay*ZFP nY{BJX$9#mz(8ZGZh$X9w-DGaIrRoBVu8bcT@R3FGKpz1B39Upv diff --git a/maildir_gtd/actions/__pycache__/next.cpython-311.pyc b/maildir_gtd/actions/__pycache__/next.cpython-311.pyc index 20a03a84637ae2a0a1d4de5e99f76042b89c36bb..e633e9626160b46a3b21ced8aecee74abb82c882 100644 GIT binary patch delta 748 zcmZWk--{Aa6h7B+#!+W<%V~H0m20{|(PqQu!nQ_5vJ}!9NDoW9EY=;$O~)Fi6dLQP zs0b3KhoAz3+5&qhgr1k({s1*WF@j!t2nzfU>P}{{^>NSP+;hI~JNG^u{V>YE+yON68Um=17$6SI(Ipc= znJzOLqi!4a0W?^(4sTmEm&`_A8P}j{GnNEEW41C7Ai#7A?#~d8-QaiuTQK|Dx<+=@ zNDP3;mVw7KXcV@&hXcU&zxBV~hV#&O8Zd|X|9uTk2-NXEh7bm2vTPaZc)$+L@)(|) z47L85+Tpg`uox;#mq}GbMJQ2=P(*v2b%)u5_MM2FDM(5oqGodEBpIubxO7d*<}XW1 zGNtPFR#c_NmXK`;~-I3)%}*@ z%}=`{Z>VSTbGZ)`@BNjUBfBbe<$Bk(^M$LLYERBJgRyon)^f!fu2@$X>j>eN5U!qW z3SwIj8-mysJeB#%e4hs4aGwQ!UPmY;$5`cyu>=CFnmMg^l1W$(z10BS&rVFtvHa6Aw`D19UuGudz?MCt PV*0w(gHF~zT4&Ku5WBV^ delta 625 zcmZ8dL2DC16n?YG&L*2~1I?I7gf`U_Od+g2B~9X?bjd|%4oiYPNYT#H#3tFAji!f9 zZ{9ovNAMs*ywroeNDg`uvHpOxE_Ctk#Z&MHs56F$_;~M|$H(`+_n0s7XL0Sdrm4W= zV_~8G^R0G)P?~*1TiPocq;fORmt1KWWE)b9nNsD38#Lg~R$q2y7w#)!PQ-ptU}ra!>}NApvu1%FW{wgi~!#7ng#*{R5syBWj3<}G(^KE>aoOi zyk@`Ct-VH}tAwbsW9hXx+2gw5uD|Bdcj2SRLJd*lpVOezt388@udCew>J#b-UMS z@1@$-VK>#X9Ov$yRY)-j>#T^^P=vK`fxW{8q_7|O8i`o#so740enplezLc#gNp+n! z^Sqp0R#kd+f9dK3WbMiy7)1Qo;xOhd=gwofaV+O&^Mg%~tokHzLDJ_WJv6l|18i*l)k)ej6lQEtN#s}(P2I*i0ie@oQUdJda$^nyOU{GNwl4bv2#k?<>8;e z)#=~k-{F6Qhri#y%m0Fm-HIw80YMje?62_HU*NHy?8clTDg@No?8xcJ%W#la(b0(c updq89(d0eMkvg2LqH}{kFaW8gAz%tOAu`#KWriXTqwxm@97K^A&>jG2wsCC$ diff --git a/maildir_gtd/actions/__pycache__/previous.cpython-311.pyc b/maildir_gtd/actions/__pycache__/previous.cpython-311.pyc index 3c074f515ef3eca964867e0eb8c86a6c6531c916..9bd94cd15b97f057f66cc93c75a3cc0a517c634e 100644 GIT binary patch delta 444 zcmZn=*v`+poR^o20SIPx3uQ=8e)-TD- zO-jv6DbaUHElbVGFGwxYPcKQ)ElDlP&CE;8(Jjf(&zbz5dB)_$Eb)vylbKm9S&R69 zmQS`}oy8f<$iUFR@PS2m@_p7S1r}cE4-7zJ0{0DJxuwDjg0HArUsSZYqG)qT*ye(; z&E!HhMINaMZWG)-urr9*e&CqAhpkUh+F6L#hbPoXie_?t6 delta 553 zcmdna-yp!doR^o20SI1N31t*c72ZPQ6d4TfD=s)%uV4yHheb2T&87=3=FG*7y=j>7#UJngBg1K zahkxH!nKBbvH+8a98M)XDZF4M;-(@ngBci97>ZPD*>LLNt6>8<2dp!ONtcm-a~G2= zqlSlmXmM&$v3^NrZc=JqN{PNpYFTPdenD!Hetv05PJVv2Zb`mwX7c3A%rm5=f-~~V zGxO3F3W`$8GV@D|6>?LHixbmRtrR9_u_Q2ZPd?3JIhm7n7H1~V#|;c0SVShTVXcy2 z<(2-x03;@G-w>8tAbds9{F1Qw1!42aZfuHL;?w;n`Cm|pJRp3EOkq|J@0K6u3$N&HU diff --git a/maildir_gtd/actions/__pycache__/show_message.cpython-311.pyc b/maildir_gtd/actions/__pycache__/show_message.cpython-311.pyc index cfd088826731155f2483b866be5a30004e2c2c9f..2f335f906598461189701901cd5ae38f551235c1 100644 GIT binary patch delta 746 zcmZuvL2DC16n?WayJ?~uX<8B+lnM=$P}-oJRYX$(hvTQ1f7h@4fln_rZJf%kIYQSGFAk z74M!T)<1Xbsrz}AyZ{OmdElcGLK+*M;o}nSW9*RoF-=6c-^I`Kgt% zsAm<0BixEYW49f?N$agzhs~w84J{p_5Xzd^52~`4NWMptgc^d#XJ{&m2aiz@(E$e7 zFb9}oN~kI9&hNz=kzp~isq_K(niwC{077G9C_&NNv32LB2NujtrB-ila^sKyM{XQhV# delta 761 zcmZuu&ubGw82x5uck^?bTA?veP;5a~+k^zGr7A^@Afl(B(p)5FMzhT(8)hdahfVe1 zKTzhNP$JSpMJ$5cKz2le38lX!F=z-t70i_de$9&-^saccvK!g|F*# zwf8$_X5+j%yaEaoInY35ghGxTT*j0zLN$t?XjFfxl@%+2W&8-$77C1#s0h2d)4Pxm zcxZuqcq>2QlLjaQf#pY(u~cP*7>LN218iwyPQwUYB;*2Gy{(iZ&`5o#ga zc7Z1QWSV9X#QfL=U*+x<`|aQ7`{{G9yYh~Hw6=F6b2AU0 zuNBnyFdr{oCWlw@#RB None: ) if result.returncode == 0: app.query_one("#main_content", Static).update(f"Message {app.current_message_id} archived.") - app.show_status(f"Message {app.current_message_id} archived.") action_next(app) # Automatically show the next message else: app.query_one("#main_content", Static).update(f"Failed to archive message {app.current_message_id}.") diff --git a/maildir_gtd/actions/next.py b/maildir_gtd/actions/next.py index 3e607aa..05ec6e2 100644 --- a/maildir_gtd/actions/next.py +++ b/maildir_gtd/actions/next.py @@ -23,13 +23,13 @@ def action_next(app) -> None: import json envelopes = json.loads(result.stdout) ids = sorted(int(envelope['id']) for envelope in envelopes) - for envelope_id in ids: - if envelope_id > app.current_message_id: - app.current_message_id = envelope_id - app.show_message(app.current_message_id) - app.show_status(f"Showing next message: {app.current_message_id}") + for index, envelope_id in enumerate(ids): + if envelope_id > int(app.current_message_id): + app.show_message(envelope_id) return + app.show_status("No newer messages found.", severity="warning") + app.show_message(envelopes[-1]['id']) # Automatically show the previous message else: app.show_status("Failed to fetch envelope list.", severity="error") except Exception as e: diff --git a/maildir_gtd/actions/open.py b/maildir_gtd/actions/open.py index b32d315..ff81268 100644 --- a/maildir_gtd/actions/open.py +++ b/maildir_gtd/actions/open.py @@ -6,7 +6,6 @@ def action_open(app) -> None: def check_id(message_id: str) -> bool: try: int(message_id) - app.show_status(f"Opening message {message_id}...") app.current_message_id = message_id app.show_message(app.current_message_id) except ValueError: diff --git a/maildir_gtd/actions/previous.py b/maildir_gtd/actions/previous.py index b7df02b..4e7ed49 100644 --- a/maildir_gtd/actions/previous.py +++ b/maildir_gtd/actions/previous.py @@ -14,10 +14,9 @@ def action_previous(app) -> None: envelopes = json.loads(result.stdout) ids = sorted((int(envelope['id']) for envelope in envelopes), reverse=True) for envelope_id in ids: - if envelope_id < app.current_message_id: + if envelope_id < int(app.current_message_id): app.current_message_id = envelope_id app.show_message(app.current_message_id) - app.show_status(f"Showing previous message: {app.current_message_id}") return app.show_status("No older messages found.", severity="warning") else: diff --git a/maildir_gtd/actions/show_message.py b/maildir_gtd/actions/show_message.py index 1efcab2..9a01eca 100644 --- a/maildir_gtd/actions/show_message.py +++ b/maildir_gtd/actions/show_message.py @@ -1,8 +1,10 @@ from textual.widgets import Static +from rich.markdown import Markdown import subprocess def show_message(app, message_id: int) -> None: """Fetch and display the email message by ID.""" + app.current_message_id = message_id app.query_one("#main_content", Static).loading = True try: result = subprocess.run( @@ -12,7 +14,6 @@ def show_message(app, message_id: int) -> None: ) if result.returncode == 0: # Render the email content as Markdown - from rich.markdown import Markdown markdown_content = Markdown(result.stdout, justify=True) app.query_one("#main_content", Static).loading = False app.query_one("#main_content", Static).update(markdown_content) diff --git a/maildir_gtd/app.py b/maildir_gtd/app.py index 5a59500..5e46e62 100644 --- a/maildir_gtd/app.py +++ b/maildir_gtd/app.py @@ -5,7 +5,8 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) import logging from typing import Iterable from textual import on -from textual.app import App, ComposeResult, SystemCommand +from textual.widget import Widget +from textual.app import App, ComposeResult, SystemCommand, RenderResult from textual.logging import TextualHandler from textual.screen import Screen from textual.widgets import Header, Footer, Static, Label, Input, Button @@ -27,13 +28,29 @@ logging.basicConfig( handlers=[TextualHandler()], ) +class Hello(Widget): + """Display a greeting.""" + + def render(self) -> RenderResult: + return "Hello, [b]World[/b]!" + +class StatusTitle(Static): + total_messages: Reactive[int] = Reactive(0) + current_message_index: Reactive[int] = Reactive(0) + current_message_id: Reactive[int] = Reactive(1) + + def render(self) -> RenderResult: + return f"Inbox Message ID: {self.current_message_id} | [b]{self.current_message_index}[/b]/{self.total_messages}" + + + class EmailViewerApp(App): """A simple email viewer app using the Himalaya CLI.""" title = "Maildir GTD Reader" - CSS_PATH = "email_viewer.tcss" # Optional: For styling - current_message_id: Reactive[int] = Reactive(1) + CSS_PATH = "email_viewer.tcss" + def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]: yield from super().get_system_commands(screen) @@ -64,12 +81,43 @@ class EmailViewerApp(App): def compose(self) -> ComposeResult: """Create child widgets for the app.""" yield Header(show_clock=True) - yield Footer(Label("[n] Next | [p] Previous | [d] Delete | [a] Archive | [o] Open | [q] Quit | [c] Create Task")) - yield ScrollableContainer(Static(Label("Email Viewer App"), id="main_content")) + yield StatusTitle().data_bind(EmailViewerApp.current_message_id) + yield ScrollableContainer(Static(Label("Loading Email Viewer App"), id="main_content")) + yield Footer() + + def watch_current_message_id(self, message_id: int, old_message_id: int) -> None: + """Called when the current message ID changes.""" + if (message_id == old_message_id): + return + # self.notify("Current message ID changed", title="Status", severity="information") + logging.info(f"Current message ID changed to {message_id}") + try: + result = subprocess.run( + ["himalaya", "envelope", "list", "-o", "json"], + capture_output=True, + text=True + ) + if result.returncode == 0: + import json + envelopes = json.loads(result.stdout) + if envelopes: + status = self.query_one(StatusTitle) + status.total_messages = len(envelopes) # Get the first envelope's ID + status.current_message_index = next( + (index + 1 for index, envelope in enumerate(envelopes) if int(envelope['id']) == message_id), + 0 # Default to 0 if no match is found + ) + else: + self.query_one("#main_content", Static).update("Failed to fetch the most recent message ID.") + except Exception as e: + self.query_one("#main_content", Static).update(f"Error: {e}") + def on_mount(self) -> None: - """Called when the app is mounted.""" self.alert_timer: Timer | None = None # Timer to throttle alerts + self.theme = "monokai" + + # self.watch(self.query_one(StatusTitle), "current_message_id", update_progress) # Fetch the ID of the most recent message using the Himalaya CLI try: result = subprocess.run( @@ -81,7 +129,7 @@ class EmailViewerApp(App): import json envelopes = json.loads(result.stdout) if envelopes: - self.current_message_id = int(envelopes[0]['id']) # Get the first envelope's ID + self.current_message_id = int(envelopes[0]['id']) else: self.query_one("#main_content", Static).update("Failed to fetch the most recent message ID.") except Exception as e: diff --git a/maildir_gtd/email_viewer.tcss b/maildir_gtd/email_viewer.tcss index b675b1a..51138c8 100644 --- a/maildir_gtd/email_viewer.tcss +++ b/maildir_gtd/email_viewer.tcss @@ -13,3 +13,11 @@ Label#task_prompt_label { Label#message_label { padding: 1; } + +StatusTitle { + width: 100%; + height: 1; + color: $text; + background: $surface; + content-align: center middle; +} diff --git a/maildir_gtd/screens/CreateTask.py b/maildir_gtd/screens/CreateTask.py index 6e05336..ddd433f 100644 --- a/maildir_gtd/screens/CreateTask.py +++ b/maildir_gtd/screens/CreateTask.py @@ -20,7 +20,6 @@ class CreateTaskScreen(Screen[str]): task_args = input_widget.value self.dismiss(task_args) - @on(Input._on_key) - def handle_close(self, event) -> None: + def on_key(self, event) -> None: if (event.key == "escape" or event.key == "ctrl+c"): self.dismiss() diff --git a/maildir_gtd/screens/__pycache__/CreateTask.cpython-311.pyc b/maildir_gtd/screens/__pycache__/CreateTask.cpython-311.pyc index 46429848bdea137cfb54c4a3d93363e67637daf6..364ca27faf286f68034c29f13399d286be7a6bb5 100644 GIT binary patch delta 266 zcmZn_Tra@8oR^o20SIQc3T0SKD`i%oz;1H+BUQf&5&T$7{NJSMMZQ(!EZe1|FW)