99import unicodedata
1010from datetime import datetime
1111from .config import ConfigManager
12+ import subprocess
1213
1314
1415class DisplayFormatter :
@@ -25,7 +26,8 @@ def __init__(self, config_manager: ConfigManager,
2526 show_account : bool = True ,
2627 show_grid : bool = True ,
2728 custom_width : Optional [int ] = None ,
28- custom_height : Optional [int ] = None ):
29+ custom_height : Optional [int ] = None ,
30+ graph_timeline : bool = False ):
2931 """Initialize the display formatter."""
3032 terminal_size = shutil .get_terminal_size ()
3133 self .terminal_width = terminal_size .columns
@@ -46,6 +48,7 @@ def __init__(self, config_manager: ConfigManager,
4648 self .show_grid = show_grid
4749 self .custom_width = custom_width
4850 self .custom_height = custom_height
51+ self .graph_timeline = graph_timeline
4952
5053 def display (self , username : str , user_data : Dict [str , Any ],
5154 stats : Dict [str , Any ], spaced = True ) -> None :
@@ -212,6 +215,15 @@ def _determine_layout(self, username: str, user_data: Dict[str, Any],
212215 def _display_minimal (self , username : str , stats : Dict [str , Any ],
213216 spaced = True ) -> None :
214217 """Display only contribution graph for narrow terminals."""
218+ if self .graph_timeline :
219+ # Show git timeline graph
220+ try :
221+ timeline_text = self ._get_graph_text ()
222+ print (timeline_text )
223+ except Exception as e :
224+ print (f"Error displaying timeline: { e } " )
225+ return
226+
215227 if not self .show_grid :
216228 # Show just the header without grid
217229 contrib_graph = stats .get ('contribution_graph' , [])
@@ -236,6 +248,30 @@ def _display_minimal(self, username: str, stats: Dict[str, Any],
236248 def _display_compact (self , username : str , user_data : Dict [str , Any ],
237249 stats : Dict [str , Any ], spaced = True ) -> None :
238250 """Display graph and minimal info side-by-side (no languages)."""
251+ if self .graph_timeline :
252+ # Show git timeline graph (only if no right side content for layout)
253+ right_side = []
254+ if self .show_account :
255+ info_lines = self ._format_user_info_compact (user_data , stats )
256+ right_side .extend (info_lines )
257+ if self .show_achievements :
258+ contrib_graph = stats .get ('contribution_graph' , [])
259+ recent_weeks = self ._get_recent_weeks (contrib_graph )
260+ achievements = self ._build_achievements (recent_weeks )
261+ if achievements :
262+ if right_side :
263+ right_side .append ("" )
264+ right_side .extend (achievements )
265+
266+ if not right_side :
267+ try :
268+ timeline_text = self ._get_graph_text ()
269+ print (timeline_text )
270+ except Exception as e :
271+ print (f"Error displaying timeline: { e } " )
272+ return
273+ # Fall back to normal display if there's right side content
274+
239275 contrib_graph = stats .get ('contribution_graph' , [])
240276 recent_weeks = self ._get_recent_weeks (contrib_graph )
241277 graph_width = max (40 , (self .terminal_width - 40 ) // 2 )
@@ -282,6 +318,37 @@ def _display_compact(self, username: str, user_data: Dict[str, Any],
282318 def _display_full (self , username : str , user_data : Dict [str , Any ],
283319 stats : Dict [str , Any ], spaced = True ) -> None :
284320 """Display full layout with graph and all info sections."""
321+ if self .graph_timeline and not self .show_grid :
322+ # Show git timeline graph (only when no grid is shown)
323+ try :
324+ timeline_text = self ._get_graph_text ()
325+ print (timeline_text )
326+ # Still show right side info
327+ contrib_graph = stats .get ('contribution_graph' , [])
328+ info_lines = (self ._format_user_info (user_data , stats )
329+ if self .show_account else [])
330+ language_lines = (self ._format_languages (stats )
331+ if self .show_languages else [])
332+ recent_weeks = self ._get_recent_weeks (contrib_graph )
333+ achievements = (self ._build_achievements (recent_weeks )
334+ if self .show_achievements else [])
335+
336+ right_side = list (info_lines )
337+ if language_lines and self .terminal_width >= 120 :
338+ right_side .append ("" )
339+ right_side .extend (language_lines )
340+ if achievements :
341+ right_side .append ("" )
342+ right_side .extend (achievements )
343+
344+ if right_side :
345+ print () # Add spacing
346+ for line in right_side :
347+ print (line )
348+ except Exception as e :
349+ print (f"Error displaying timeline: { e } " )
350+ return
351+
285352 contrib_graph = stats .get ('contribution_graph' , [])
286353 graph_width = max (50 , (self .terminal_width - 10 ) // 2 )
287354
@@ -440,6 +507,23 @@ def _get_contribution_graph_lines(self, weeks_data: list,
440507
441508 return lines
442509
510+ def _get_graph_text (vertical = False ):
511+ text = subprocess .check_output (
512+ ['git' , '--no-pager' , 'log' , '--graph' , '--all' , '--pretty=format:""' ]).decode ().replace ('"' , '' )
513+ if vertical :
514+ return text
515+ text = text .translate (str .maketrans (
516+ r"\/" , r"\/" [::- 1 ])).replace ("|" , "-" )
517+ lines = text .splitlines ()
518+ max_len = max (len (line ) for line in lines )
519+ padded = [line .ljust (max_len ) for line in lines ]
520+
521+ rotated = []
522+ for col in reversed (range (max_len )):
523+ new_line = '' .join (padded [row ][col ] for row in range (len (padded )))
524+ rotated .append (new_line )
525+ return '\n ' .join (rotated )
526+
443527 def _format_user_info_compact (self , user_data : Dict [str , Any ],
444528 stats : Dict [str , Any ]) -> list :
445529 """Format minimal user info for compact layout."""
0 commit comments