@@ -8,10 +8,10 @@ import (
88 "path/filepath"
99 "strings"
1010
11+ "github.com/dfanso/commit-msg/src/chatgpt"
1112 "github.com/dfanso/commit-msg/src/gemini"
1213 "github.com/dfanso/commit-msg/src/grok"
1314 "github.com/dfanso/commit-msg/src/types"
14- "github.com/dfanso/commit-msg/src/chatgpt"
1515 "github.com/pterm/pterm"
1616)
1717
@@ -68,22 +68,48 @@ func main() {
6868 Path : currentDir ,
6969 }
7070
71+ // Get file statistics before fetching changes
72+ fileStats , err := getFileStatistics (& repoConfig )
73+ if err != nil {
74+ log .Fatalf ("Failed to get file statistics: %v" , err )
75+ }
76+
77+ // Display header
78+ pterm .DefaultHeader .WithFullWidth ().
79+ WithBackgroundStyle (pterm .NewStyle (pterm .BgDarkGray )).
80+ WithTextStyle (pterm .NewStyle (pterm .FgLightWhite )).
81+ Println ("🚀 Commit Message Generator" )
82+
83+ pterm .Println ()
84+
85+ // Display file statistics with icons
86+ displayFileStatistics (fileStats )
87+
88+ if fileStats .TotalFiles == 0 {
89+ pterm .Warning .Println ("No changes detected in the Git repository." )
90+ return
91+ }
92+
7193 // Get the changes
7294 changes , err := getGitChanges (& repoConfig )
7395 if err != nil {
7496 log .Fatalf ("Failed to get Git changes: %v" , err )
7597 }
7698
7799 if len (changes ) == 0 {
78- fmt .Println ("No changes detected in the Git repository." )
100+ pterm . Warning .Println ("No changes detected in the Git repository." )
79101 return
80102 }
81103
82- spinnerGenerating , err := pterm .DefaultSpinner .Start ("Generating commit message..." )
104+ pterm .Println ()
105+
106+ // Show generating spinner
107+ spinnerGenerating , err := pterm .DefaultSpinner .
108+ WithSequence ("⠋" , "⠙" , "⠹" , "⠸" , "⠼" , "⠴" , "⠦" , "⠧" , "⠇" , "⠏" ).
109+ Start ("🤖 Generating commit message..." )
83110 if err != nil {
84111 log .Fatalf ("Failed to start spinner: %v" , err )
85112 }
86- defer spinnerGenerating .Stop ()
87113
88114 var commitMsg string
89115 if os .Getenv ("COMMIT_LLM" ) == "google" {
@@ -93,17 +119,23 @@ func main() {
93119 } else {
94120 commitMsg , err = grok .GenerateCommitMessage (config , changes , apiKey )
95121 }
96-
97122
98123 if err != nil {
99- log .Fatalf ("Failed to generate commit message: %v" , err )
124+ spinnerGenerating .Fail ("Failed to generate commit message" )
125+ log .Fatalf ("Error: %v" , err )
100126 }
101127
102- spinnerGenerating .Success ()
128+ spinnerGenerating .Success ("✅ Commit message generated successfully!" )
129+
130+ pterm .Println ()
103131
104- // Display the commit message
105- pterm .DefaultBasicText .Println ()
106- pterm .DefaultBasicText .Println (commitMsg )
132+ // Display the commit message in a styled panel
133+ displayCommitMessage (commitMsg )
134+
135+ pterm .Println ()
136+
137+ // Display changes preview
138+ displayChangesPreview (fileStats )
107139}
108140
109141// Check if directory is a git repository
@@ -237,3 +269,203 @@ func isSmallFile(filename string) bool {
237269
238270 return info .Size () <= maxSize
239271}
272+
273+ // FileStatistics holds statistics about changed files
274+ type FileStatistics struct {
275+ StagedFiles []string
276+ UnstagedFiles []string
277+ UntrackedFiles []string
278+ TotalFiles int
279+ LinesAdded int
280+ LinesDeleted int
281+ }
282+
283+ // Get file statistics for display
284+ func getFileStatistics (config * types.RepoConfig ) (* FileStatistics , error ) {
285+ stats := & FileStatistics {
286+ StagedFiles : []string {},
287+ UnstagedFiles : []string {},
288+ UntrackedFiles : []string {},
289+ }
290+
291+ // Get staged files
292+ stagedCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--name-only" , "--cached" )
293+ stagedOutput , err := stagedCmd .Output ()
294+ if err == nil && len (stagedOutput ) > 0 {
295+ stats .StagedFiles = strings .Split (strings .TrimSpace (string (stagedOutput )), "\n " )
296+ }
297+
298+ // Get unstaged files
299+ unstagedCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--name-only" )
300+ unstagedOutput , err := unstagedCmd .Output ()
301+ if err == nil && len (unstagedOutput ) > 0 {
302+ stats .UnstagedFiles = strings .Split (strings .TrimSpace (string (unstagedOutput )), "\n " )
303+ }
304+
305+ // Get untracked files
306+ untrackedCmd := exec .Command ("git" , "-C" , config .Path , "ls-files" , "--others" , "--exclude-standard" )
307+ untrackedOutput , err := untrackedCmd .Output ()
308+ if err == nil && len (untrackedOutput ) > 0 {
309+ stats .UntrackedFiles = strings .Split (strings .TrimSpace (string (untrackedOutput )), "\n " )
310+ }
311+
312+ // Filter empty strings
313+ stats .StagedFiles = filterEmpty (stats .StagedFiles )
314+ stats .UnstagedFiles = filterEmpty (stats .UnstagedFiles )
315+ stats .UntrackedFiles = filterEmpty (stats .UntrackedFiles )
316+
317+ stats .TotalFiles = len (stats .StagedFiles ) + len (stats .UnstagedFiles ) + len (stats .UntrackedFiles )
318+
319+ // Get line statistics from staged changes
320+ if len (stats .StagedFiles ) > 0 {
321+ statCmd := exec .Command ("git" , "-C" , config .Path , "diff" , "--cached" , "--numstat" )
322+ statOutput , err := statCmd .Output ()
323+ if err == nil {
324+ lines := strings .Split (strings .TrimSpace (string (statOutput )), "\n " )
325+ for _ , line := range lines {
326+ parts := strings .Fields (line )
327+ if len (parts ) >= 2 {
328+ if added := parts [0 ]; added != "-" {
329+ var addedNum int
330+ fmt .Sscanf (added , "%d" , & addedNum )
331+ stats .LinesAdded += addedNum
332+ }
333+ if deleted := parts [1 ]; deleted != "-" {
334+ var deletedNum int
335+ fmt .Sscanf (deleted , "%d" , & deletedNum )
336+ stats .LinesDeleted += deletedNum
337+ }
338+ }
339+ }
340+ }
341+ }
342+
343+ return stats , nil
344+ }
345+
346+ // Filter empty strings from slice
347+ func filterEmpty (slice []string ) []string {
348+ filtered := []string {}
349+ for _ , s := range slice {
350+ if s != "" {
351+ filtered = append (filtered , s )
352+ }
353+ }
354+ return filtered
355+ }
356+
357+ // Display file statistics with colored output
358+ func displayFileStatistics (stats * FileStatistics ) {
359+ pterm .DefaultSection .Println ("📊 Changes Summary" )
360+
361+ // Create bullet list items
362+ bulletItems := []pterm.BulletListItem {}
363+
364+ if len (stats .StagedFiles ) > 0 {
365+ bulletItems = append (bulletItems , pterm.BulletListItem {
366+ Level : 0 ,
367+ Text : pterm .Green (fmt .Sprintf ("✅ Staged files: %d" , len (stats .StagedFiles ))),
368+ TextStyle : pterm .NewStyle (pterm .FgGreen ),
369+ BulletStyle : pterm .NewStyle (pterm .FgGreen ),
370+ })
371+ for i , file := range stats .StagedFiles {
372+ if i < 5 { // Show first 5 files
373+ bulletItems = append (bulletItems , pterm.BulletListItem {
374+ Level : 1 ,
375+ Text : file ,
376+ })
377+ }
378+ }
379+ if len (stats .StagedFiles ) > 5 {
380+ bulletItems = append (bulletItems , pterm.BulletListItem {
381+ Level : 1 ,
382+ Text : pterm .Gray (fmt .Sprintf ("... and %d more" , len (stats .StagedFiles )- 5 )),
383+ })
384+ }
385+ }
386+
387+ if len (stats .UnstagedFiles ) > 0 {
388+ bulletItems = append (bulletItems , pterm.BulletListItem {
389+ Level : 0 ,
390+ Text : pterm .Yellow (fmt .Sprintf ("⚠️ Unstaged files: %d" , len (stats .UnstagedFiles ))),
391+ TextStyle : pterm .NewStyle (pterm .FgYellow ),
392+ BulletStyle : pterm .NewStyle (pterm .FgYellow ),
393+ })
394+ for i , file := range stats .UnstagedFiles {
395+ if i < 3 {
396+ bulletItems = append (bulletItems , pterm.BulletListItem {
397+ Level : 1 ,
398+ Text : file ,
399+ })
400+ }
401+ }
402+ if len (stats .UnstagedFiles ) > 3 {
403+ bulletItems = append (bulletItems , pterm.BulletListItem {
404+ Level : 1 ,
405+ Text : pterm .Gray (fmt .Sprintf ("... and %d more" , len (stats .UnstagedFiles )- 3 )),
406+ })
407+ }
408+ }
409+
410+ if len (stats .UntrackedFiles ) > 0 {
411+ bulletItems = append (bulletItems , pterm.BulletListItem {
412+ Level : 0 ,
413+ Text : pterm .Cyan (fmt .Sprintf ("📝 Untracked files: %d" , len (stats .UntrackedFiles ))),
414+ TextStyle : pterm .NewStyle (pterm .FgCyan ),
415+ BulletStyle : pterm .NewStyle (pterm .FgCyan ),
416+ })
417+ for i , file := range stats .UntrackedFiles {
418+ if i < 3 {
419+ bulletItems = append (bulletItems , pterm.BulletListItem {
420+ Level : 1 ,
421+ Text : file ,
422+ })
423+ }
424+ }
425+ if len (stats .UntrackedFiles ) > 3 {
426+ bulletItems = append (bulletItems , pterm.BulletListItem {
427+ Level : 1 ,
428+ Text : pterm .Gray (fmt .Sprintf ("... and %d more" , len (stats .UntrackedFiles )- 3 )),
429+ })
430+ }
431+ }
432+
433+ pterm .DefaultBulletList .WithItems (bulletItems ).Render ()
434+ }
435+
436+ // Display commit message in a styled panel
437+ func displayCommitMessage (message string ) {
438+ pterm .DefaultSection .Println ("📝 Generated Commit Message" )
439+
440+ // Create a panel with the commit message
441+ panel := pterm .DefaultBox .
442+ WithTitle ("Commit Message" ).
443+ WithTitleTopCenter ().
444+ WithBoxStyle (pterm .NewStyle (pterm .FgLightGreen )).
445+ WithHorizontalString ("─" ).
446+ WithVerticalString ("│" ).
447+ WithTopLeftCornerString ("┌" ).
448+ WithTopRightCornerString ("┐" ).
449+ WithBottomLeftCornerString ("└" ).
450+ WithBottomRightCornerString ("┘" )
451+
452+ panel .Println (pterm .LightGreen (message ))
453+ }
454+
455+ // Display changes preview
456+ func displayChangesPreview (stats * FileStatistics ) {
457+ pterm .DefaultSection .Println ("🔍 Changes Preview" )
458+
459+ // Create info boxes
460+ if stats .LinesAdded > 0 || stats .LinesDeleted > 0 {
461+ infoData := [][]string {
462+ {"Lines Added" , pterm .Green (fmt .Sprintf ("+%d" , stats .LinesAdded ))},
463+ {"Lines Deleted" , pterm .Red (fmt .Sprintf ("-%d" , stats .LinesDeleted ))},
464+ {"Total Files" , pterm .Cyan (fmt .Sprintf ("%d" , stats .TotalFiles ))},
465+ }
466+
467+ pterm .DefaultTable .WithHasHeader (false ).WithData (infoData ).Render ()
468+ } else {
469+ pterm .Info .Println ("No line statistics available for unstaged changes" )
470+ }
471+ }
0 commit comments