"""Weather quad: wttr.in rendered with its original ANSI/CLI art (via AnsiRenderer). Reuses the shared LocationService for the city; if none is known yet, wttr.in geolocates the caller's IP itself, so the widget still works standalone. The 'ascii_art' feature toggle swaps the art for a compact one-line text summary. """ from __future__ import annotations import gi gi.require_version("Gtk", "4.0") from gi.repository import Gtk # noqa: E402 from lib.ansi import AnsiRenderer from lib.proc import run_text from module_base import Feature, ModuleContext, ModuleInstance, ModuleSpec from paths import BACKEND_DIR _SCRIPT = str(BACKEND_DIR / "weather.sh") class _WeatherView(Gtk.Box): def __init__(self, ctx: ModuleContext, opts: str): super().__init__(orientation=Gtk.Orientation.VERTICAL) self.add_css_class("weather-view") self.ctx = ctx self.opts = opts self._loc = "" self.renderer = AnsiRenderer() self.append(self.renderer.view) self._status = Gtk.Label(label="Loading weather…") self._status.add_css_class("weather-status") self.append(self._status) ctx.services.location.subscribe(self._on_location) self.refresh() def _on_location(self, data: dict) -> None: self._loc = data.get("city") or "" self.refresh() def refresh(self) -> None: art = self.ctx.feature("ascii_art", True) opts = self.opts if art else "format=%l:+%c+%t,+%w" run_text([_SCRIPT, self._loc, opts], self._on_result) def _on_result(self, ok: bool, out: str, err: str) -> None: if ok and out.strip() and " ModuleInstance: # The ascii_art toggle rebuilds the whole card via QuadCard, so both views are # recreated and re-fetch with the current format — no partial refresh here. compact = _WeatherView(ctx, opts="0") # current conditions only expanded = _WeatherView(ctx, opts="") # full 3-day forecast return ModuleInstance(compact=compact, expanded=expanded) SPEC = ModuleSpec( id="weather", title="Weather", icon="", # nf-weather build=build, features=[Feature("ascii_art", "CLI art", True)], )