import { Gtk } from "ags/gtk4"; import GLib from "gi://GLib"; import Gio from "gi://Gio"; import { watchFile } from "../../lib/fileMonitor"; interface WeatherData { current: { temp: number; wind: number; icon: string; }; today: { high: number; low: number; rain_pct: number; icon: string; }; forecast: Array<{ day: string; high: number; low: number; rain: number; icon: string; }>; } export default function WeatherWidget() { const cachePath = GLib.build_filenamev([ GLib.get_user_cache_dir(), "ags", "weather.json", ]); // --- Current --- const currentIcon = new Gtk.Label({ cssClasses: ["weather-current-icon"] }); const currentTemp = new Gtk.Label({ cssClasses: ["weather-current-temp"] }); const currentWind = new Gtk.Label({ cssClasses: ["weather-current-wind"] }); // --- Today summary --- const todayIcon = new Gtk.Label({ cssClasses: ["weather-today-icon"] }); const todayHighLow = new Gtk.Label({ cssClasses: ["weather-today-highlow"] }); const rainPill = new Gtk.Label({ cssClasses: ["weather-rain-pill"] }); // --- Forecast --- const forecastBox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, homogeneous: true, cssClasses: ["weather-forecast-box"], }); function makeForecastDay(day: WeatherData["forecast"][number]) { const wrap = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, cssClasses: ["weather-forecast-day"], }); wrap.append(new Gtk.Label({ label: day.day, cssClasses: ["weather-forecast-label"] })); wrap.append(new Gtk.Label({ label: day.icon, cssClasses: ["weather-forecast-icon"] })); wrap.append(new Gtk.Label({ label: `${Math.round(day.high)}° ${Math.round(day.low)}°`, cssClasses: ["weather-forecast-temps"], })); // Rain bar const barBg = new Gtk.Box({ cssClasses: ["weather-rain-bar-bg"], heightRequest: 4 }); const barFg = new Gtk.Box({ cssClasses: ["weather-rain-bar-fg"], heightRequest: 4, widthRequest: Math.round(day.rain * 0.6), // max ~60px at 100% }); barBg.append(barFg); wrap.append(barBg); wrap.append(new Gtk.Label({ label: `${day.rain}%`, cssClasses: ["weather-forecast-rain"], })); return wrap; } function updateDisplay(data: WeatherData) { // Current currentIcon.label = data.current.icon; currentTemp.label = `${Math.round(data.current.temp)}°`; currentWind.label = `💨 ${data.current.wind} m/s`; // Today todayIcon.label = data.today.icon; todayHighLow.label = `▲ ${Math.round(data.today.high)}° ▼ ${Math.round(data.today.low)}°`; rainPill.label = `🌧 ${data.today.rain_pct}%`; // Forecast let child = forecastBox.get_first_child(); while (child) { const next = child.get_next_sibling(); forecastBox.remove(child); child = next; } data.forecast.forEach(d => forecastBox.append(makeForecastDay(d))); } function readWeatherFile(): WeatherData | null { try { const file = Gio.File.new_for_path(cachePath); if (!file.query_exists(null)) return null; const [ok, contents] = file.load_contents(null); if (ok) return JSON.parse(new TextDecoder("utf-8").decode(contents)); } catch (e) { log(`WeatherWidget: ${e}`); } return null; } const initial = readWeatherFile(); if (initial) updateDisplay(initial); // Build the root widget const root = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, cssClasses: ["grid-card", "weather-card"], hexpand: true, }); watchFile(cachePath, () => { const d = readWeatherFile(); if (d) updateDisplay(d); }, root); // Build the UI structure const currentRow = new Gtk.Box({ cssClasses: ["weather-current-row"], halign: Gtk.Align.CENTER, }); currentRow.append(currentIcon); const currentInfoBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, valign: Gtk.Align.CENTER, }); currentInfoBox.append(currentTemp); currentInfoBox.append(currentWind); currentRow.append(currentInfoBox); root.append(currentRow); root.append(new Gtk.Box({ vexpand: true })); const sep1 = new Gtk.Box({ cssClasses: ["weather-sep"], heightRequest: 1, hexpand: true }); root.append(sep1); const todayRow = new Gtk.Box({ cssClasses: ["weather-today-row"], halign: Gtk.Align.CENTER, }); todayRow.append(todayIcon); const todayInfoBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, valign: Gtk.Align.CENTER, }); todayInfoBox.append(todayHighLow); todayInfoBox.append(rainPill); todayRow.append(todayInfoBox); root.append(todayRow); root.append(new Gtk.Box({ vexpand: true })); const sep2 = new Gtk.Box({ cssClasses: ["weather-sep"], heightRequest: 1, hexpand: true }); root.append(sep2); root.append(forecastBox); return root; }