@@ -3,9 +3,12 @@ package main
33import (
44 "bufio"
55 "context"
6+ "encoding/json"
67 "fmt"
8+ "io"
79 "math"
810 "math/rand"
11+ "net/http"
912 "os"
1013 "os/exec"
1114 "runtime"
@@ -36,6 +39,34 @@ type SystemInfo struct {
3639 Processes int
3740 LoadAvg string
3841 Username string
42+ Weather string
43+ }
44+
45+ // Config holds all configuration options
46+ type Config struct {
47+ // Display settings
48+ FPS int `json:"fps"`
49+ ColorScheme string `json:"color_scheme"`
50+ ShowCPU bool `json:"show_cpu"`
51+ ShowMemory bool `json:"show_memory"`
52+ ShowDisk bool `json:"show_disk"`
53+ ShowUptime bool `json:"show_uptime"`
54+ ShowKernel bool `json:"show_kernel"`
55+ ShowOS bool `json:"show_os"`
56+ ShowHostname bool `json:"show_hostname"`
57+
58+ // Frame / animation settings
59+ FrameFile string `json:"frame_file"`
60+ LoopAnimation bool `json:"loop_animation"`
61+ CenterContent bool `json:"center_content"`
62+
63+ // Output mode
64+ StaticMode bool `json:"static_mode"`
65+ HideAnimation bool `json:"hide_animation"`
66+
67+ // Misc
68+ ShowFPSCounter bool `json:"show_fps_counter"`
69+ ShowWeather bool `json:"show_weather"`
3970}
4071
4172// Model represents the Bubble Tea model
@@ -45,6 +76,7 @@ type Model struct {
4576 frameRate time.Duration
4677 startTime time.Time
4778 sysInfo SystemInfo
79+ config Config
4880 ctx context.Context
4981 cancel context.CancelFunc
5082 width int
@@ -333,25 +365,35 @@ func (m Model) renderSystemInfo() string {
333365 info .WriteString (lipgloss .NewStyle ().Foreground (lipgloss .Color ("240" )).Render ("─────────────────────" ))
334366 info .WriteString ("\n \n " )
335367
336- info .WriteString (fmt .Sprintf ("%s %s\n " ,
337- infoStyle .Render ("OS:" ),
338- valueStyle .Render (fmt .Sprintf ("%s (%s)" , m .sysInfo .OS , m .sysInfo .Architecture ))))
368+ if m .config .ShowOS {
369+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
370+ infoStyle .Render ("OS:" ),
371+ valueStyle .Render (fmt .Sprintf ("%s (%s)" , m .sysInfo .OS , m .sysInfo .Architecture ))))
372+ }
339373
340- info .WriteString (fmt .Sprintf ("%s %s\n " ,
341- infoStyle .Render ("User:" ),
342- valueStyle .Render (m .sysInfo .Username )))
374+ if m .config .ShowHostname {
375+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
376+ infoStyle .Render ("User:" ),
377+ valueStyle .Render (m .sysInfo .Username )))
378+ }
343379
344- info .WriteString (fmt .Sprintf ("%s %s\n " ,
345- infoStyle .Render ("CPU:" ),
346- valueStyle .Render (fmt .Sprintf ("%d cores" , m .sysInfo .CPUCount ))))
380+ if m .config .ShowCPU {
381+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
382+ infoStyle .Render ("CPU:" ),
383+ valueStyle .Render (fmt .Sprintf ("%d cores" , m .sysInfo .CPUCount ))))
384+ }
347385
348- info .WriteString (fmt .Sprintf ("%s %s\n " ,
349- infoStyle .Render ("Memory:" ),
350- valueStyle .Render (m .sysInfo .Memory )))
386+ if m .config .ShowMemory {
387+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
388+ infoStyle .Render ("Memory:" ),
389+ valueStyle .Render (m .sysInfo .Memory )))
390+ }
351391
352- info .WriteString (fmt .Sprintf ("%s %s\n " ,
353- infoStyle .Render ("Go Version:" ),
354- valueStyle .Render (m .sysInfo .GoVersion )))
392+ if m .config .ShowKernel {
393+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
394+ infoStyle .Render ("Go Version:" ),
395+ valueStyle .Render (m .sysInfo .GoVersion )))
396+ }
355397
356398 if m .sysInfo .Processes > 0 {
357399 info .WriteString (fmt .Sprintf ("%s %s\n " ,
@@ -365,9 +407,11 @@ func (m Model) renderSystemInfo() string {
365407 valueStyle .Render (strings .TrimPrefix (m .sysInfo .LoadAvg , "Load: " ))))
366408 }
367409
368- info .WriteString (fmt .Sprintf ("%s %s\n " ,
369- infoStyle .Render ("Disk:" ),
370- valueStyle .Render (strings .TrimPrefix (m .sysInfo .DiskUsage , "Disk: " ))))
410+ if m .config .ShowDisk {
411+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
412+ infoStyle .Render ("Disk:" ),
413+ valueStyle .Render (strings .TrimPrefix (m .sysInfo .DiskUsage , "Disk: " ))))
414+ }
371415
372416 // Runtime information
373417 info .WriteString ("\n " )
@@ -376,18 +420,28 @@ func (m Model) renderSystemInfo() string {
376420 info .WriteString (lipgloss .NewStyle ().Foreground (lipgloss .Color ("240" )).Render ("─────────────────────" ))
377421 info .WriteString ("\n \n " )
378422
379- uptime := time .Since (m .startTime )
380- info .WriteString (fmt .Sprintf ("%s %s\n " ,
381- infoStyle .Render ("Uptime:" ),
382- valueStyle .Render (uptime .Truncate (time .Second ).String ())))
423+ if m .config .ShowUptime {
424+ uptime := time .Since (m .startTime )
425+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
426+ infoStyle .Render ("Uptime:" ),
427+ valueStyle .Render (uptime .Truncate (time .Second ).String ())))
428+ }
383429
384430 info .WriteString (fmt .Sprintf ("%s %s\n " ,
385431 infoStyle .Render ("Time:" ),
386432 valueStyle .Render (time .Now ().Format ("15:04:05" ))))
387433
388- info .WriteString (fmt .Sprintf ("%s %s\n " ,
389- infoStyle .Render ("FPS:" ),
390- valueStyle .Render (fmt .Sprintf ("%.1f" , float64 (time .Second )/ float64 (m .frameRate )))))
434+ if m .config .ShowWeather {
435+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
436+ infoStyle .Render ("Weather:" ),
437+ valueStyle .Render (strings .TrimPrefix (m .sysInfo .Weather , "Weather: " ))))
438+ }
439+
440+ if m .config .ShowFPSCounter {
441+ info .WriteString (fmt .Sprintf ("%s %s\n " ,
442+ infoStyle .Render ("FPS:" ),
443+ valueStyle .Render (fmt .Sprintf ("%.1f" , float64 (time .Second )/ float64 (m .frameRate )))))
444+ }
391445
392446 return info .String ()
393447}
@@ -517,6 +571,9 @@ func GetSystemInfo() SystemInfo {
517571 // Get load average (Unix-like systems)
518572 info .LoadAvg = getLoadAverage ()
519573
574+ // Get weather information
575+ info .Weather = getWeather ()
576+
520577 return info
521578}
522579
@@ -920,14 +977,113 @@ func extractColor(line string) lipgloss.Color {
920977 return lipgloss .Color ("252" )
921978}
922979
980+ // getWeather gets weather information from wttr.in
981+ func getWeather () string {
982+ client := & http.Client {
983+ Timeout : 5 * time .Second ,
984+ }
985+
986+ resp , err := client .Get ("https://wttr.in/?format=%C+%t" )
987+ if err != nil {
988+ return "Weather: N/A"
989+ }
990+ defer resp .Body .Close ()
991+
992+ if resp .StatusCode != http .StatusOK {
993+ return "Weather: N/A"
994+ }
995+
996+ body , err := io .ReadAll (resp .Body )
997+ if err != nil {
998+ return "Weather: N/A"
999+ }
1000+
1001+ weather := strings .TrimSpace (string (body ))
1002+ if weather == "" {
1003+ return "Weather: N/A"
1004+ }
1005+
1006+ return fmt .Sprintf ("Weather: %s" , weather )
1007+ }
1008+
1009+ // getDefaultConfig returns the default configuration
1010+ func getDefaultConfig () Config {
1011+ return Config {
1012+ // Display settings
1013+ FPS : 5 ,
1014+ ColorScheme : "blue" ,
1015+ ShowCPU : true ,
1016+ ShowMemory : true ,
1017+ ShowDisk : true ,
1018+ ShowUptime : true ,
1019+ ShowKernel : true ,
1020+ ShowOS : true ,
1021+ ShowHostname : true ,
1022+
1023+ // Frame / animation settings
1024+ FrameFile : "default" ,
1025+ LoopAnimation : true ,
1026+ CenterContent : true ,
1027+
1028+ // Output mode
1029+ StaticMode : false ,
1030+ HideAnimation : false ,
1031+
1032+ // Misc
1033+ ShowFPSCounter : false ,
1034+ ShowWeather : false ,
1035+ }
1036+ }
1037+
1038+ // loadConfig loads configuration from file or creates default
1039+ func loadConfig () (Config , error ) {
1040+ configPath := "gophetch.json"
1041+
1042+ // Check if config file exists
1043+ if _ , err := os .Stat (configPath ); os .IsNotExist (err ) {
1044+ // Create default config file
1045+ defaultConfig := getDefaultConfig ()
1046+ data , err := json .MarshalIndent (defaultConfig , "" , " " )
1047+ if err != nil {
1048+ return defaultConfig , fmt .Errorf ("failed to marshal default config: %v" , err )
1049+ }
1050+
1051+ if err := os .WriteFile (configPath , data , 0644 ); err != nil {
1052+ return defaultConfig , fmt .Errorf ("failed to write default config: %v" , err )
1053+ }
1054+
1055+ fmt .Printf ("Created default config file: %s\n " , configPath )
1056+ return defaultConfig , nil
1057+ }
1058+
1059+ // Load existing config
1060+ data , err := os .ReadFile (configPath )
1061+ if err != nil {
1062+ return getDefaultConfig (), fmt .Errorf ("failed to read config file: %v" , err )
1063+ }
1064+
1065+ var config Config
1066+ if err := json .Unmarshal (data , & config ); err != nil {
1067+ return getDefaultConfig (), fmt .Errorf ("failed to parse config file: %v" , err )
1068+ }
1069+
1070+ return config , nil
1071+ }
1072+
9231073func main () {
1074+ // Load configuration
1075+ config , err := loadConfig ()
1076+ if err != nil {
1077+ fmt .Printf ("Warning: %v, using defaults\n " , err )
1078+ config = getDefaultConfig ()
1079+ }
1080+
9241081 var frames []Frame
925- var err error
926- frameRate := 200 * time .Millisecond
1082+ frameRate := time .Duration (1000 / config .FPS ) * time .Millisecond
9271083
928- // Parse command line arguments
1084+ // Load frames based on config or command line arguments
9291085 if len (os .Args ) > 1 {
930- // Check if first argument is a filename (contains .txt or doesn't parse as duration)
1086+ // Command line arguments override config
9311087 if strings .Contains (os .Args [1 ], ".txt" ) || strings .Contains (os .Args [1 ], ".frames" ) {
9321088 // Load frames from file
9331089 filename := os .Args [1 ]
@@ -953,6 +1109,19 @@ func main() {
9531109 frameRate = duration
9541110 }
9551111 }
1112+ } else {
1113+ // Use config file setting
1114+ if config .FrameFile != "default" && config .FrameFile != "" {
1115+ fmt .Printf ("Loading frames from config file: %s\n " , config .FrameFile )
1116+ frames , err = LoadFramesFromFile (config .FrameFile )
1117+ if err != nil {
1118+ fmt .Printf ("Error loading config frame file: %v\n " , err )
1119+ fmt .Printf ("Falling back to rain animation...\n " )
1120+ frames = []Frame {} // Use rain animation as fallback
1121+ } else {
1122+ fmt .Printf ("Successfully loaded %d frames from config\n " , len (frames ))
1123+ }
1124+ }
9561125 }
9571126
9581127 // If no frames loaded, use rain animation
@@ -970,6 +1139,7 @@ func main() {
9701139 frameRate : frameRate ,
9711140 startTime : time .Now (),
9721141 sysInfo : GetSystemInfo (),
1142+ config : config ,
9731143 ctx : ctx ,
9741144 cancel : cancel ,
9751145 mutex : & sync.RWMutex {},
0 commit comments