aboutsummaryrefslogtreecommitdiff
path: root/wttrin.el
Commit message (Collapse)AuthorAgeFilesLines
* fix: persist runtime favorite and saved locations in a state filerelease/0.4.0Craig Jennings2026-07-021-33/+134
| | | | | | | | savehist rewrites its whole file on every save, keeping only the variables registered in the running process. Any Emacs that saved savehist without wttrin loaded (a batch tool, a session that never opened wttrin) silently deleted the persisted favorite and saved-location entries. A d-set default then reverted to the init value on the next restart. The runtime favorite and directory now live in wttrin-state-file, written only by wttrin: write-through on every location command, atomic temp-file + rename, UTF-8 pinned both ways, read with read (never eval), versioned plist. Values restore at load and re-assert on savehist-mode-hook, so a stale legacy savehist restore can't clobber them. With no state file present, legacy savehist values are adopted and written once, so existing users migrate with zero steps. Search history stays on savehist. It's scrub-tolerant throwaway data, exactly what savehist is for. Also: the README documents the mode-line tooltip forecast option, and the gitignore covers one more local tooling artifact.
* feat(mode-line): optional multi-day forecast in the weather tooltipCraig Jennings2026-07-011-3/+127
| | | | The tooltip showed current conditions only. I added wttrin-mode-line-tooltip-forecast-days (default 0, capped at wttr.in's three days). When positive, the mode-line refresh also fetches ?format=j1 and caches the parsed day list, and the tooltip renders one line per day between the conditions and the age line: Today / Tomorrow / weekday label, min-max temps in the configured unit, and the midday description. A failed or malformed forecast fetch keeps the previous forecast and never disturbs the main fetch.
* fix: center the loading placeholder on an a switchCraig Jennings2026-07-011-8/+20
| | | | | | wttrin-query inserted the "Loading weather for ..." placeholder but never centered it, leaving that to window-configuration-change-hook. On an `a` switch the *wttr.in* buffer is already displayed, so the hook doesn't fire. The placeholder kept the previous weather block's window margin and rendered off-center until the new weather arrived. I moved the placeholder rendering into wttrin--render-loading-placeholder, which centers it explicitly via wttrin--update-layout. It no longer depends on the hook, so the placeholder is centered on the first open and through every switch.
* fix: keep a d-set favorite from reverting to the init value on restartCraig Jennings2026-07-011-71/+137
| | | | | | | | | | wttrin-favorite-location did two jobs: a defcustom the user sets in init, and the savehist-persisted value that `d`/make-default mutated. Setting it in init (setopt) clobbered the `d` choice on every startup. So a favorite promoted with `d` reverted to the init value after a restart, and the mode-line showed the wrong location's weather. wttrin-saved-locations had the same defect. I split the two roles. The defcustoms stay as the init-set base. New savehist-persisted runtime vars, wttrin--favorite-override and wttrin--saved-locations-runtime, hold what `d`, save, rename, remove, and the geolocation commands write. Reads go through resolvers: wttrin--favorite-location returns the override or the configured favorite, and wttrin--saved-locations overlays the runtime directory on the configured one (runtime wins on a name collision). Init and `d` now write different variables, so neither clobbers the other. Removing a saved location that only comes from init can't delete it, since the runtime removal has nothing to touch. The command now says so instead of reporting a removal that didn't happen. There's no automatic migration. A favorite set in init is unaffected, and a favorite set only with `d` before this version is re-set once with `d`. The README documents the behavior.
* fix: don't auto-fit the loading placeholder to a huge fontCraig Jennings2026-06-281-1/+28
| | | | | | | With auto-fit on, the one-line "Loading..." placeholder was sized to fill the window height (capped huge), and a reused buffer kept the previous weather's auto-fitted font while loading. Auto-fit now runs only once real weather has rendered, and the placeholder resets to the base font.
* fix: don't steal focus when an async weather response rendersCraig Jennings2026-06-281-1/+5
| | | | | | | | wttrin--display-weather ran switch-to-buffer from the async callback, so a response arriving after the user moved to another buffer yanked them back to *wttr.in*. It now renders with set-buffer; selecting the buffer is the interactive command's job (wttrin-query already does it at invocation), so a late response updates the buffer in place without changing the selected window.
* fix: drop stale async weather responses that overwrite a newer queryCraig Jennings2026-06-281-2/+14
| | | | | | | wttrin-query reuses the single *wttr.in* buffer, so requesting Paris then Berlin and having Paris return last overwrote the Berlin buffer. Each query now stamps a monotonic request id on the buffer; the callback displays only when its captured id still matches, so a superseded response is ignored.
* fix: stop the mode-line cleanly when disabled before startup firesCraig Jennings2026-06-281-2/+29
| | | | | | | | | The delayed initial fetch was scheduled with run-at-time but the one-shot timer was discarded, and when the mode is enabled before after-init the start is queued on after-init-hook. Disabling the mode before either fired could still start timers, hit the network, or mutate mode-line state after the user turned it off. Stop now cancels the stored startup timer and removes the after-init hook, and the startup callback no-ops when the mode is off.
* fix: always evict at least one cache entry when over maxCraig Jennings2026-06-281-3/+4
| | | | | | wttrin--cleanup-cache-if-needed removed floor(count * 0.20) entries, which is 0 for a small cache (e.g. two entries at max one), so the cache could sit over its advertised maximum indefinitely. It now removes at least one whenever it is over.
* fix: key the cache on all response-shaping settingsCraig Jennings2026-06-281-2/+7
| | | | | | | | | | | wttrin--make-cache-key keyed only on location and unit system, but the request body also depends on wttrin-display-options and wttrin-default-languages. Changing display options or language mid-session served the old cached response, and the staleness header then claimed it was current for the new settings. The key now includes every setting that shapes the response. Cache-key tests rewritten to assert behavior (distinct key per varying setting, stable key for identical settings) instead of an exact key string.
* fix: require subr-x for string-trimCraig Jennings2026-06-281-0/+1
| | | | | wttrin.el calls string-trim throughout but never required subr-x, so a clean load could hit a void-function before something else pulled subr-x in.
* chore: clean up package metadataCraig Jennings2026-06-281-1/+1
| | | | | Drop the "games" keyword (it miscategorizes a weather tool) from wttrin.el and Eask. Sync wttrin-geolocation.el's version to 0.3.2 to match the main module.
* style: normalize wttrin-default-locations indentation to spacesCraig Jennings2026-06-281-5/+5
| | | | | The list mixed tabs and spaces. Aligned every entry on spaces, no behavior change.
* refactor: extract the auto-fit input guard, fix a unit-stale docstringCraig Jennings2026-06-281-7/+14
| | | | | | | | Pull the eight-condition validity check in wttrin--fit-font-height into wttrin--fit-inputs-usable-p, so the arithmetic branch reads as one line. Reword wttrin--center-margin's docstring, which still said "column counts" after the pixel-based centering rewrite started passing it pixels.
* feat: fit and center the weather buffer in its windowCraig Jennings2026-06-281-8/+146
| | | | | | | | | | | | | | | | | | Centering and auto-fit share one resize hook (wttrin--update-layout on window-configuration-change-hook). Centering is always on. The block centers via the window's left margin, measured in pixels from the buffer font so it stays exact under a font-height remap. It centers against the available width including the current margin, so the margin shrinking the body can't make the value oscillate. Auto-fit is opt-in (wttrin-auto-fit-font, off by default). When on, the font is sized so the whole buffer fits top to bottom (down to the row past the footer's last menu item) without the widest line truncating, clamped to wttrin-font-height-min and -max. The pixel-exact measurement needs window-font-width and window-font-height, so the minimum is now Emacs 25.1.
* fix: drop the alias query from history when making it the defaultHEADmainCraig Jennings2026-06-261-0/+2
| | | | Making a location the default saved it and dropped the favorite name from history, but not the underlying query. For an alias whose query differs from its name, that query lingered as a separate history candidate. Both saving branches now drop the name and the query, matching wttrin-save-location.
* fix: keep saved locations and search history disjointCraig Jennings2026-06-261-1/+13
| | | | | | Pressing d cleaned the location from history, but saving with s did not. A place saved under a friendly name kept its raw query as a separate history candidate, and removing the saved entry let that query resurface in the picker. wttrin-save-location now drops the saved name and its query from history, and wttrin-remove-location drops both the removed name and its query (captured before removal). A new wttrin--drop-from-location-history helper does the work. A place now lives in the directory or in history, never both.
* feat: save the location to the directory when making it the defaultCraig Jennings2026-06-261-5/+8
| | | | | | Pressing d on a typed or aliased location set the favorite but never added it to wttrin-saved-locations, so the default persisted only as the favorite string, not as a named directory entry. The coordinate branch already saved. The typed/alias branch did not. The typed/alias branch now puts the location into the directory before promoting it, so a typed default like "Reykjavik" persists as a ("Reykjavik" . "Reykjavik") entry. An empty-name coordinate detection still keeps the raw coordinates without saving an entry.
* docs: use a public landmark address in saved-location examplesCraig Jennings2026-06-261-1/+1
|
* feat: add named-locations directory with display namesCraig Jennings2026-06-261-113/+370
| | | | | | | | A saved location carries a display name distinct from its query target ("Mom's House" maps to coordinates or an address), stored in a savehist-persisted wttrin-saved-locations alist. The name shows everywhere the place appears (picker, buffer header, mode-line tooltip) while wttr.in is still queried by the target. Management commands wttrin-save-location, wttrin-rename-location, and wttrin-remove-location edit the directory, with refuse-on-collision rename and a favorite-fallback warning on remove. In the weather buffer, s/r/x reach those commands and d names a detected location before promoting it to the default. The footer is two columns: a "This view" column (another, refresh, quit) and a "Saved locations" column (save, make default, rename, remove). The buffer anchors to the top so the forecast isn't scrolled out of view in a short window. The mode-line follows favorite changes immediately rather than waiting for the next scheduled fetch, and its hover tooltip shows the saved name instead of the raw query. History holds named entries only: raw coordinate detections and saved names stay out of it.
* feat: add external-command geolocation, opt-out, and example adaptersCraig Jennings2026-06-251-8/+47
| | | | | | | | | | | | These build on the current-location picker with the rest of the geolocation work. wttrin-geolocation-command runs a command that prints {lat,lng} (and optionally an address) and queries wttr.in by those coordinates. IP geolocation only finds the network's exit point, which is wrong on a VPN or hotspot. A command that scans nearby WiFi resolves to street level. It runs asynchronously, falls back to the IP provider when unset or failing, and assumes nothing about the OS, so it's inert until set. When the command returns an address, wttrin shows it on a "Location:" line, so the resolved place is readable even though the fetch is by raw coordinates. wttrin-use-current-location is a labeled command that sets the favorite to auto-detect, so the bare t value never has to be typed into init by hand. wttrin-geolocation-enabled (default t) turns every geolocation surface off for anyone who wants that: the picker entry, the auto-detect favorite, and the command. examples/geolocation/ ships two reference adapters for the command: google-geolocate.py (Google API, key via the environment or ~/.authinfo.gpg) and apple-wps.py (Apple's keyless WiFi positioning, which uses an undocumented endpoint, so read its caveat). Both are Python 3 standard library and scan via nmcli, with notes on adapting the scan to other systems.
* feat: add current-location detection to the weather pickerCraig Jennings2026-06-251-32/+118
| | | | | | | | | | Geolocation used to live in a separate command, so getting weather for where you are meant knowing a second command existed. I added a "Current location (detect)" entry pinned to the top of the M-x wttrin picker. Selecting it detects your location and shows its weather. If the guess is wrong (VPN, hotspot) the detected city is visible in the header, so you reopen the picker and type one. Press d to keep the detected city as the default. The entry is pinned first through a completion table whose display-sort-function holds against completion frameworks that re-sort, and it never enters location history or the cache as a place. A new wttrin-geolocation-enabled (default t) turns every geolocation surface off for users who want that: the picker entry, the auto-detect favorite, and the command. It stays on by default. The older wttrin-set-location-from-geolocation command is now obsolete in favor of the picker. Its customize-save-variable persistence advice is replaced by savehist, which already carries the favorite across sessions.
* fix: refresh mode-line weather when the default location changesCraig Jennings2026-06-251-2/+15
| | | | | | Pressing d to promote a displayed location to the favorite changed wttrin-favorite-location but left the mode-line untouched. Its cache still held the old location's weather. The next scheduled refetch could be up to an hour away, so the hover tooltip kept showing the previous default. wttrin--set-favorite-location now detects a real change and, when the mode-line is active, clears the stale cache and fetches the new location at once via a new wttrin--mode-line-refresh-now helper. The tooltip switches within seconds instead of waiting for the timer.
* feat: add 'd' key to make the displayed location the defaultCraig Jennings2026-06-241-6/+42
| | | | | | | | | | I bound d in the weather buffer to wttrin-make-default, which sets wttrin-favorite-location to the location on screen so it drives the mode-line and future sessions. The footer advertises "[d] to make default". Persistence rides savehist, not the Emacs custom-variable mechanism: wttrin--savehist-register registers wttrin-favorite-location alongside the search history, at load and on savehist-save-hook. Enable savehist-mode and the favorite survives restarts. Promoting a location drops it from the search history, the way wttrin-default-locations entries are kept out of history. The favorite shows in the picker instead: wttrin--completion-candidates prepends it when it's a string and not already a default, so it appears exactly once. The setter only assigns the variable and trims history. It doesn't register with savehist itself, because savehist-additional-variables is unbound until savehist loads, so a direct add-to-list would error for users without savehist. Registration stays on the load and save-hook path.
* feat: expose themeable faces for mode-line and buffer textCraig Jennings2026-06-231-12/+49
| | | | | | | | | | I added four customizable faces so themes and customize-face can restyle the text wttrin draws itself: wttrin-mode-line-stale (the dimmed stale mode-line emoji), wttrin-staleness-header (the "Last updated:" line), wttrin-instructions (the footer prose), and wttrin-key (the [a]/[g]/[q] chords). The package exposed no faces before and hardcoded one color. The stale-emoji dimming used a literal "gray60". Now it inherits a face, so the color tracks the theme. I changed make-emoji-icon's second argument from a color string to a face symbol applied via :inherit. wttrin-key inherits bold rather than help-key-binding, which is Emacs 28+ while the package supports 24.4. The weather ASCII art stays colored by xterm-color's ANSI faces. Only the package's own text is newly faced.
* fix: keep location history registered after a savehist setqCraig Jennings2026-06-211-1/+9
| | | | | | | | A one-time add-to-list put wttrin--location-history into savehist-additional-variables when savehist loaded. Any user who later setqs that list, a common config pattern for curating which histories persist, replaces it and drops the entry. The search history then silently never saves. Re-assert the registration on savehist-save-hook, which runs at the top of every savehist-save. The variable is now in the list whenever a save happens, regardless of init order or a clobbering setq. Restore was never affected, since savehist reloads the value from explicit setq forms in its file. The README persistence note now reflects this: the variable stays registered even if you set savehist-additional-variables yourself.
* feat: add persistent location search historyCraig Jennings2026-06-211-2/+58
| | | | | | | | | | Successful searches are now remembered and offered as completion candidates the next time you run wttrin, listed after the configured defaults. Only successful fetches are saved, so typos and not-found locations never enter the history. A location already in wttrin-default-locations isn't duplicated into it. The list is capped at wttrin-location-history-max (default 20), oldest entries falling off first. Persistence uses savehist: the history variable is registered with savehist-additional-variables, so enabling savehist-mode carries it across restarts with no custom file I/O. Without savehist it lasts the session. Two commands manage the list: wttrin-remove-location-history drops one entry, wttrin-clear-location-history clears all. I don't pass the history variable as the completing-read history argument, because the minibuffer would then save every typed string, including failed lookups. It stays a curated candidate source, updated only on success. I trim with butlast rather than seq-take to keep the Emacs 24.4 baseline.
* feat: add typed error hierarchy for fetch failuresCraig Jennings2026-06-211-8/+52
| | | | | | | | | | Define a wttrin-error condition with children wttrin-invalid-input, wttrin-network-error, wttrin-not-found-error, wttrin-service-error, and wttrin-parse-error, so callers branch on the class of a failure instead of matching message text. Synchronous paths signal these directly: a nil query and an unknown geolocation provider now raise wttrin-invalid-input. The async fetch path can't signal across its callback, so it tags the error string with the class via a wttrin-error-type text property. The wttrin-error-message-type accessor reads it back, and two-arg callbacks are untouched. Retyping the classifier also closed two gaps: a missing status and a 2xx with an empty body used to go silent or get mislabeled "Unexpected HTTP status". Both are now parse errors. wttrin-geolocation.el now requires wttrin for the shared conditions. It's only ever loaded through wttrin, so the require is a no-op in practice and just makes the dependency explicit.
* feat: support t for auto-detect via geolocation in wttrin-favorite-locationCraig Jennings2026-05-051-32/+105
| | | | | | | | | | | | | | | | | | Completes the three-mode configuration the favorite-location feature was always meant to have: - nil — disabled (default; unchanged) - a string — explicit location (unchanged) - t — auto-detect via IP geolocation (NEW) When the user sets `wttrin-favorite-location` to t, wttrin runs the geolocation lookup once on first use and caches the result for the session. Subsequent reads return the cached string. The lookup happens in the background via the existing `wttrin-geolocation-detect`, so Emacs startup is never blocked. I added two private state vars (`wttrin--resolved-favorite-location`, `wttrin--favorite-location-pending`) and a resolver `wttrin--resolve-favorite-location` that maps the three modes onto a returned string or nil. When t is set and the cache is empty, the resolver kicks off the lookup and returns nil for that call — the next consumer tick after the callback completes gets the cached string. The pending flag prevents duplicate concurrent lookups when several consumers ask during the resolution window. Five consumer call sites now go through the resolver instead of reading `wttrin-favorite-location` directly: `wttrin--mode-line-fetch-weather`, `wttrin-mode-line-click`, `wttrin-mode-line-force-refresh`, `wttrin--buffer-cache-refresh`, and `wttrin--mode-line-start`. Two display sites (the placeholder and error tooltips) use a new `wttrin--favorite-location-display-name` helper that returns "current location" while a t-mode lookup is pending, instead of showing the literal `t` to the user. Tests cover the resolver across all three modes, including the pending state, the duplicate-suppression behavior, and detection-failure retry. Existing consumer tests stay green because the resolver returns the bound string unchanged when the variable is a string. One care: the test file requires wttrin-geolocation up front so cl-letf mocks of `wttrin-geolocation-detect` aren't undone by the resolver's lazy require — without that, the first run hit ipapi.co for real. README documents the new mode under "Setting the Favorite Location from IP Geolocation".
* feat: add wttrin-display-options for wttr.in flag customization (closes #3)Craig Jennings2026-05-051-1/+25
| | | | | | | | | | | | wttr.in accepts single-character flags appended to the URL that control what the report looks like — no Follow line (F), narrow output (n), quiet mode (q), forecast horizon (0/1/2), console-glyph mode (d), and so on. Until now wttrin always used the same default report shape with no way to opt into these. Added a `wttrin-display-options` defcustom that takes a string of concatenated flags, e.g. "0Fq" for current weather only with no Follow line and no header. The flags get appended to every request via `wttrin--build-url`. The defcustom defaults to nil so existing users see no change. I excluded `A` and `T` from the recommended set in the docstring since wttrin needs ANSI output for the xterm-color rendering to produce the colored glyphs. The user could still pass them, but the docstring nudges them away. Tests cover the normal cases (single, multi-flag, with and without unit system), the boundaries (nil and empty string both leave the URL unchanged from baseline, single character works), and a sanity check that the new flags slot in after the always-on `A`. Existing build-url tests stay green because they don't bind the new variable, and the default is nil. Also added `.claude/` to .gitignore — the scheduled-tasks lockfile from local wakeup scheduling shouldn't be tracked.
* docs: explain mode-line state and update flowCraig Jennings2026-04-261-0/+20
| | | | | | Four variables hold the mode-line's runtime state: the cache, the rendered string, the stale-render flag, and the refresh timer. They get updated in a specific order across three different functions. Anyone reading `wttrin--mode-line-tooltip` for the first time hits a clever bit on every hover: the tooltip re-evaluates staleness and triggers a re-render of the icon if it flipped. Without a comment that ties this to the rest of the update flow, you have to trace through three other functions to understand why. The new comment block above the defvars lays out the order and the hover-driven re-render in one place, so future readers don't have to reconstruct it from the call graph.
* refactor: extract wttrin--mode-line-extract-emoji helperCraig Jennings2026-04-261-4/+10
| | | | | | | | The regex that pulls the emoji character out of a wttr.in mode-line response was inlined inside `wttrin--mode-line-update-display`, mixed in with the render logic. Six tests of the parser couldn't be written without invoking the whole render path. The new pure helper takes the weather string, runs the regex, and returns either the first non-whitespace character after the colon or "?" as a placeholder. The format-explanation comment that used to sit above the inline code is gone now that the same explanation lives in the helper's docstring. There's no risk of comment and code drifting apart. Six tests cover Normal (typical response, different emoji), Boundary (no whitespace after colon, multiple whitespace chars), and Error (no colon, empty string).
* refactor: extract wttrin--mode-line-stale-p helperCraig Jennings2026-04-261-5/+13
| | | | | | | | The staleness check `(> age (* 2 wttrin-mode-line-refresh-interval))` lived in two places, `wttrin--mode-line-tooltip` and `wttrin--mode-line-update-display`, along with the four-line preamble that read the timestamp out of the cache cons and computed age. Centralizing the rule in a single helper means the threshold lives in one spot. That makes it easy to add a `wttrin-mode-line-staleness-threshold` defcustom later if the magic 2× ever needs to be tunable. The helper takes a cache entry (or nil) and returns t/nil. Five new tests cover Normal (fresh, stale) and Boundary (just below the threshold, just past, nil entry). The boundary tests use 199s and 201s against a 100s refresh interval to lock the strict `>` semantics with comfortable float-time margins. In `wttrin--mode-line-update-display` the refactor also drops two locals (`timestamp` and `age`) that were no longer used after the helper call replaced the inline calculation. Behavior is unchanged at both call sites.
* bump version to 0.3.2v0.3.2Craig Jennings2026-04-261-1/+1
|
* fix: omit :foreground from emoji icon face when no color providedCraig Jennings2026-04-261-4/+7
| | | | | | The font branch of wttrin--make-emoji-icon built its face plist with `:foreground foreground` even when foreground was nil, producing a literal `(:family ... :height 1.0 :foreground nil)`. The mode-line redisplays many times per second, and Emacs validates faces on every redisplay. A single fresh-cache state produced hundreds of "Invalid face attribute :foreground nil" warnings in *Messages*. The bug's been live since 2026-02-21 (b74b98f). The 130bbc07 helper extraction kept it in place. Switch to backquote splicing so the :foreground key is included only when foreground is non-nil. Behavior is identical on the colored path. On the nil path the emoji renders with the default mode-line color, the same visible result as before, without the warning flood.
* feat: add IP geolocation command for setting wttrin-favorite-locationCraig Jennings2026-04-221-0/+34
| | | | | | | | | | | | Lets users set `wttrin-favorite-location` by IP lookup instead of typing a city by hand. `M-x wttrin-set-location-from-geolocation` runs the lookup, shows the detected "City, Region" in a yes/no prompt, and on confirmation sets the variable for the session. The docstring points at `M-x customize-save-variable` for persistence across restarts. The new `wttrin-geolocation.el` module provides the provider layer. Three providers come built in: ipapi.co (the default), ipinfo.io, and ipwho.is. All three are HTTPS, need no API key, and have free tiers large enough for interactive use. The module has three layers. Pure JSON parsers handle the per-provider quirks: ipapi's `error: true` flag, ipwho.is's `success: false` flag, ipinfo's HTTP-status-only signalling. A small fetch helper extracts the HTTP body. `wttrin-geolocation-detect` wires them together and calls back with "City, Region" on success, or nil on any failure (network error, HTTP 4xx or 5xx, malformed response, rate-limit signal). Providers live in an alist keyed by symbol, with plist values for :name, :url, and :parser. To use a different provider, push an entry onto `wttrin-geolocation--providers` and select it via `wttrin-geolocation-provider`. No code change needed. README gains a subsection under Mode-line Weather Display covering the command, how to persist the result, provider selection with free-tier limits, and the accuracy caveat for VPN or mobile-hotspot users. 39 new tests across the parser layer (10 ipapi, 6 ipinfo, 6 ipwhois), fetch-and-dispatch (11), and interactive command (6). Each suite covers Normal, Boundary, and Error categories. Tests mock `url-retrieve` and `yes-or-no-p` at their boundaries and run the real extract-and-parse pipeline underneath. Test suite: 333 → 373 passing.
* bump version to 0.3.1v0.3.1Craig Jennings2026-04-041-1/+1
|
* fix: weather ASCII art wraps when window is narrowCraig Jennings2026-04-041-0/+2
| | | | | Enable truncate-lines in wttrin-mode so the fixed-width weather art clips at the window edge instead of wrapping and breaking the layout.
* fix: weather buffer shows location in lowercaseCraig Jennings2026-04-041-0/+4
| | | | | | wttr.in returns "Weather report: new orleans, la" regardless of query casing. Replace the lowercase location on the header line with the user's original string after rendering.
* feat: specific error messages for fetch failuresCraig Jennings2026-04-041-29/+65
| | | | | | | | | | | | | | Add HTTP status code checking (wttrin--extract-http-status) and pass error descriptions through the callback chain so users see "Location not found (HTTP 404)" or "Network error — check your connection" instead of the generic "Perhaps the location was misspelled?" for every failure. Also fix pre-existing bug where the condition-case error handler in extract-response-body killed an unrelated buffer after unwind-protect already cleaned up. 330 tests (was 307), all passing.
* bump version to 0.3.0v0.3.0Craig Jennings2026-04-041-1/+1
| | | | | | Dynamic mode-line tooltip, staleness dimming, location casing fix, debug guard refactor, expanded test suite (307 tests), and multiple bug fixes since 0.2.3.
* fix: clean byte-compilation warningsCraig Jennings2026-04-041-2/+2
| | | | | | | Move define-obsolete-variable-alias before its referent defcustom to fix "alias should be declared before its referent" warning. Add defvar for wttrin--force-refresh in wttrin-debug.el so the byte-compiler knows the dynamic variable is intentional.
* doc: note wttr.in update frequency in refresh interval docstringsCraig Jennings2026-04-041-2/+5
| | | | | | wttr.in updates its data roughly every 10 minutes (max-age=600). Add a note to both refresh interval defcustoms so users know not to poll more aggressively than the service can provide.
* doc: improve source comments for senior developer audienceCraig Jennings2026-04-041-23/+21
| | | | | | | | | | Remove obvious comments that restate what the code does ("save debug data if enabled", "temporarily allow editing", "align buffer to top"). Add comments explaining non-obvious decisions: why risky-local-variable is needed, why user-agent is curl, what wttr.in format codes mean, what the emoji extraction regex matches, and why after-init-time matters. Fix stale docstrings that no longer match the no-op stub pattern.
* fix: emoji dimming can disagree with tooltip stalenessCraig Jennings2026-04-041-0/+10
| | | | | | | | | | The emoji face (dimmed/normal) was frozen at the last update-display call, but the tooltip computes staleness dynamically. Between refreshes, data could cross the stale threshold — tooltip says "Stale" while the emoji is still normal. Track the rendered staleness state. When the tooltip detects a transition, trigger a re-render so the emoji dimming matches.
* fix: cache timestamp reflects request time, not response timeCraig Jennings2026-04-041-3/+2
| | | | | | | float-time was captured in the outer let* before the async fetch. The callback used this stale value, making cache entries appear slightly older than they are. Move the float-time call into the callback so the timestamp reflects when the data actually arrived.
* fix: validate-weather-data corrupts match dataCraig Jennings2026-04-041-1/+1
| | | | | string-match modifies global match data as a side effect. A predicate should not do this. Use string-match-p instead.
* fix: mode-line tooltip always shows "Updated just now"Craig Jennings2026-04-041-14/+26
| | | | | | | | | | | The tooltip was a static string computed at fetch time. Since every successful fetch sets the cache timestamp to now and immediately renders the tooltip, it was always "just now". Extract wttrin--mode-line-tooltip as a named function that computes age from the cache at call time. Set help-echo to this function so Emacs invokes it on hover, producing an accurate age like "Updated 12 minutes ago".
* fix: mode-line tooltip shows location in lowercaseCraig Jennings2026-04-041-1/+11
| | | | | | | wttr.in's %l format returns locations in lowercase (e.g., "new orleans, la") regardless of the query casing. Replace the API's location prefix with the user's original wttrin-favorite-location string at cache time so tooltips display what the user expects.
* doc: update copyright years to 2026 and add missing author fieldCraig Jennings2026-04-041-1/+1
| | | | | Update copyright headers across all 37 .el files to include 2026. Add missing Author field to testutil-wttrin.el for consistency.