package output import ( "fmt" "strconv" "strings" "time" ) // --------------------------------------------------------------------- // RGB STRUCTS & HELPERS // --------------------------------------------------------------------- type RGB struct { R int G int B int } func (c RGB) ANSI() string { return fmt.Sprintf("\033[38;2;%d;%d;%dm", c.R, c.G, c.B) } func RGBFromInts(r, g, b int) RGB { return RGB{R: r, G: g, B: b} } func RGBFromHex(hex string) RGB { hex = strings.TrimPrefix(hex, "#") if len(hex) != 6 { return RGB{255, 255, 255} // fail-safe } r, _ := strconv.ParseInt(hex[0:2], 16, 0) g, _ := strconv.ParseInt(hex[2:4], 16, 0) b, _ := strconv.ParseInt(hex[4:6], 16, 0) return RGB{int(r), int(g), int(b)} } func Blend(a, b RGB, factor float64) RGB { return RGB{ R: int(float64(a.R)*(1-factor) + float64(b.R)*factor), G: int(float64(a.G)*(1-factor) + float64(b.G)*factor), B: int(float64(a.B)*(1-factor) + float64(b.B)*factor), } } // --------------------------------------------------------------------- // TIME-OF-DAY COLOUR PHASES (CANADIAN ENGLISH) // --------------------------------------------------------------------- // These were recreated from the original Python _get_time_phase logic. type Phase struct { Name string Primary RGB // top gradient Secondary RGB // bottom gradient } func getTimePhase() Phase { h := time.Now().Hour() switch { case h >= 5 && h < 8: return Phase{ Name: "dawn", Primary: RGBFromHex("#FFB478"), // orange-peach morning sky Secondary: RGBFromHex("#8C64A0"), // purple base } case h >= 8 && h < 17: return Phase{ Name: "day", Primary: RGBFromHex("#4696FF"), // bright sky blue Secondary: RGBFromHex("#78BEFF"), // lighter blue } case h >= 17 && h < 20: return Phase{ Name: "sunset", Primary: RGBFromHex("#FF6E3C"), // deep orange Secondary: RGBFromHex("#643C78"), // muted purple } default: return Phase{ Name: "night", Primary: RGBFromHex("#323C64"), // deep navy Secondary: RGBFromHex("#141E3C"), // darker base } } } // Public so other modules (logo, TUI, Telefact) can use it. func GetTimePhaseColours() (RGB, RGB, string) { phase := getTimePhase() return phase.Primary, phase.Secondary, phase.Name } // --------------------------------------------------------------------- // WEATHER TONES — CANADIAN ENGLISH (GREY, COLOUR, METRE, etc.) // --------------------------------------------------------------------- // This is used as a tint on top of the base time-of-day palette. var WeatherTones = map[string]RGB{ "sunny": RGBFromHex("#FFD700"), // gold "clear": RGBFromHex("#FFD700"), "cloudy": RGBFromHex("#B4B4B4"), "mostly_cloudy": RGBFromHex("#B4B4B4"), "rain": RGBFromHex("#2870C8"), "drizzle": RGBFromHex("#2870C8"), "snow": RGBFromHex("#E6E6F0"), "fog": RGBFromHex("#82828C"), "mist": RGBFromHex("#82828C"), "thunderstorm": RGBFromHex("#C878FF"), "smoke": RGBFromHex("#8C7864"), } // Normalises Environment Canada text into our tone keys. func NormaliseCondition(cond string) string { c := strings.ToLower(cond) switch { case strings.Contains(c, "sun"): return "sunny" case strings.Contains(c, "clear"): return "clear" case strings.Contains(c, "snow"): return "snow" case strings.Contains(c, "storm"): return "thunderstorm" case strings.Contains(c, "rain"): return "rain" case strings.Contains(c, "drizzle"): return "drizzle" case strings.Contains(c, "fog"): return "fog" case strings.Contains(c, "mist"): return "mist" case strings.Contains(c, "cloud"): return "cloudy" case strings.Contains(c, "smoke"): return "smoke" } return "clear" }