feat: kanban status buttons, task back-nav, RSS UI, weather search, briefing fixes

Project view:
- Add inline status advance buttons on kanban task cards (todo→in_progress,
  in_progress→done); buttons reveal on hover, stop link navigation

Task viewer:
- Back button navigates to task's project instead of /tasks when project_id set
- Esc key navigates to project (or /tasks); blurs focused element first

Quick capture:
- Use user's configured model instead of hardcoded Config.OLLAMA_MODEL
- Remove create_project from classifier prompt (tool not offered, caused
  task-shaped inputs to silently fall through to note fallback)

Briefing scheduler:
- Fix get_event_loop() → get_running_loop() so background thread uses the
  correct hypercorn event loop (jobs were scheduling but never executing)
- Suppress bare greeting when both LLM synthesis lanes return empty

RSS feed UI (SettingsView):
- Show last-fetched age, category badge, and feed URL per row
- Category input field when adding a feed
- Refresh all button: fetches latest items, reloads list, toasts with count
- Enter key submits add-feed form; better empty-state hint with example feeds

Weather tool:
- Accept any city/region name in addition to 'home'/'work'/'all'
- Geocodes via Nominatim + fetches live from Open-Meteo for arbitrary queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 00:42:01 -04:00
parent a9414cf949
commit a2ba90160c
9 changed files with 238 additions and 34 deletions
+32 -9
View File
@@ -530,16 +530,19 @@ _CORE_TOOLS = [
"function": {
"name": "get_weather",
"description": (
"Get the current 7-day weather forecast for the user's configured locations. "
"Returns temperature, precipitation, and conditions for each day. "
"Also reports any changes since the last forecast was fetched."
"Get the 7-day weather forecast. Pass a city/region name to look up any location, "
"or omit to get the user's configured home/work locations. "
"Returns temperature, precipitation, and conditions for each day."
),
"parameters": {
"type": "object",
"properties": {
"location_key": {
"location": {
"type": "string",
"description": "Which location to focus on: 'home', 'work', or 'all'. Defaults to 'all'.",
"description": (
"City, region, or country to look up — e.g. 'Portland, Oregon', 'Tokyo', 'home', 'work'. "
"Use 'home' or 'work' to get the user's saved locations. Omit for all saved locations."
),
}
},
"required": [],
@@ -1646,11 +1649,31 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
return {"success": True, "log": log.to_dict(), "task": note.title}
elif tool_name == "get_weather":
from fabledassistant.services.weather import get_cached_weather
location_key = arguments.get("location_key", "all")
from fabledassistant.services.weather import (
get_cached_weather, geocode, _fetch_open_meteo, parse_forecast
)
from datetime import datetime, timezone as _tz
location = (arguments.get("location") or "").strip()
_known = {"home", "work", "all", ""}
if location.lower() not in _known:
# Live geocode + fetch for arbitrary city
try:
lat, lon, label = await geocode(location)
raw = await _fetch_open_meteo(lat, lon)
days = parse_forecast(raw)
return {"data": {"locations": [{
"location_key": "query",
"location_label": label,
"fetched_at": datetime.now(_tz.utc).isoformat(),
"days": days,
"changes_since_last_fetch": [],
}]}}
except Exception as e:
return {"success": False, "error": f"Could not get weather for '{location}': {e}"}
# Fall back to cached configured locations
locations = await get_cached_weather(user_id)
if location_key != "all":
locations = [loc for loc in locations if loc["location_key"] == location_key]
if location.lower() not in ("all", ""):
locations = [loc for loc in locations if loc["location_key"] == location.lower()]
return {"data": {"locations": locations}}
elif tool_name == "get_rss_items":