44
55import sys
66import time
7+ import webbrowser
8+ import urllib .request
79from dataclasses import dataclass , field
810from typing import Any , Callable
911
1012from rich import box
1113from rich .console import Console
1214from rich .layout import Layout
1315from rich .live import Live
16+ from rich .markdown import Markdown
1417from rich .panel import Panel
1518from rich .table import Table
1619from rich .text import Text
@@ -53,6 +56,9 @@ class TUIState:
5356 t : float = 0.0
5457 show_size_info : bool = False
5558 size_info : str = ""
59+ show_changelog : bool = False
60+ changelog_content : str = ""
61+ changelog_scroll_pos : int = 0
5662
5763
5864class TUIApp :
@@ -177,6 +183,15 @@ def _handle(self, key: str) -> None:
177183 def _handle_nav (self , key : str ) -> None :
178184 state = self ._state
179185
186+ if state .show_changelog :
187+ if key == "\x1b [A" : # Up
188+ state .changelog_scroll_pos = max (0 , state .changelog_scroll_pos - 1 )
189+ elif key == "\x1b [B" : # Down
190+ state .changelog_scroll_pos += 1
191+ elif key in ("c" , "C" ):
192+ state .show_changelog = False
193+ return
194+
180195 if key == "\x1b [A" :
181196 state .current_field = (state .current_field - 1 ) % _NUM_FIELDS
182197 elif key == "\x1b [B" :
@@ -187,6 +202,23 @@ def _handle_nav(self, key: str) -> None:
187202 self ._nudge (+ 1 )
188203 elif key == "\x1b [D" :
189204 self ._nudge (- 1 )
205+ elif key in ("c" , "C" ):
206+ if not state .changelog_content :
207+ try :
208+ with urllib .request .urlopen ("https://raw.githubusercontent.com/programmersd21/bangen/refs/heads/main/CHANGELOG.md" ) as response :
209+ content = response .read ().decode ("utf-8" )
210+ lines = content .splitlines ()
211+ if lines and lines [0 ].strip () == "# Changelog" :
212+ content = "\n " .join (lines [1 :]).strip ()
213+ state .changelog_content = content
214+ state .changelog_scroll_pos = 0
215+ except Exception as exc :
216+ state .status = f"Changelog error: { exc } "
217+ if state .changelog_content :
218+ state .show_changelog = not state .show_changelog
219+ elif key in ("i" , "I" ):
220+ webbrowser .open ("https://github.com/programmersd21/bangen/issues/new" )
221+ state .status = "Opened issues page in browser"
190222 elif key in ("e" , "E" ):
191223 try :
192224 self .open_export_dialog ()
@@ -198,7 +230,6 @@ def _handle_nav(self, key: str) -> None:
198230 except Exception as exc :
199231 state .status = f"Preset loader unavailable: { exc } "
200232 elif key in ("a" , "A" ):
201- # Toggle auto-size info display
202233 state .show_size_info = not state .show_size_info
203234 if state .show_size_info :
204235 state .status = "Auto-size info: ON"
@@ -293,8 +324,18 @@ def _quick_save(self) -> None:
293324 def _build_layout (self ):
294325 if self .active_modal is not None :
295326 return self .active_modal .render ()
327+ if self ._state .show_changelog :
328+ lines = self ._state .changelog_content .splitlines ()
329+ display_lines = lines [self ._state .changelog_scroll_pos :]
330+ return Panel (
331+ Markdown ("\n " .join (display_lines )),
332+ title = "[bold cyan]Changelog[/bold cyan]" ,
333+ subtitle = "[dim]ββ to scroll, 'c' to close[/dim]" ,
334+ box = box .ROUNDED ,
335+ )
296336 return self ._build_main_layout ()
297337
338+
298339 def _build_main_layout (self ) -> Layout :
299340 layout = Layout ()
300341 layout .split_row (Layout (name = "ctrl" , ratio = 1 ), Layout (name = "prev" , ratio = 2 ))
@@ -477,6 +518,8 @@ def _help_line() -> Text:
477518 ("ββ" , "navigate" ),
478519 ("ββ" , "adjust" ),
479520 ("Enter" , "edit/toggle" ),
521+ ("c" , "changelog" ),
522+ ("i" , "report issue" ),
480523 ("l" , "load presets" ),
481524 ("a" , "size info" ),
482525 ("e" , "export" ),
0 commit comments