Files
sankalp 3a81ab1212 feat(spotlight): add plugin system with core engine and examples
- Plugin API (plugin.py, context.py, result.py)
- Core engine (search.py, manager.py, registry.py, loader.py)
- Built-in plugins: app, calculator, clipboard, emoji, power_menu, query, wallpapers
- Spotlight docs and example plugins
- Spotlight CSS styling
2026-07-12 20:17:34 +05:45

129 lines
3.5 KiB
Python

"""Example: package plugin with third-party dependencies.
Demonstrates:
- Package plugin structure
- Third-party deps via requirements.txt (uv venv auto-install)
- Keyword activation + global search
- Deep reload during development
- Cancellation checks for I/O
- External command support
Usage:
cp -r examples/plugins/weather config/plugins/weather
Set your API key:
env = OPENWEATHER_API_KEY, your_key_here
Open spotlight and type "wth london" or just "london".
After editing, deep-reload:
fabric-cli exec modus1 'deep_reload weather'
"""
import os
from typing import Any
from window.spotlight.api import SearchResult, SpotlightPlugin
try:
import requests
except ImportError:
requests = None # type: ignore[assignment]
class WeatherPlugin(SpotlightPlugin):
id = "weather"
name = "Weather"
icon = "weather-clear-symbolic"
keywords = ["wth", "weather"]
searchable = True
priority = 75
def __init__(self, context):
super().__init__(context)
self._cache: dict | None = None
def initialize(self) -> None:
pass
def cleanup(self) -> None:
self._cache = None
def release_memory(self) -> None:
self._cache = None
def search(self, query: str, token: Any) -> list[SearchResult]:
q = query.strip().lower()
for prefix in ("wth ", "weather "):
if q.startswith(prefix):
q = q[len(prefix) :].strip()
break
if q in ("wth", "weather"):
q = ""
if not q:
return [
SearchResult(
id="weather_hint",
title="Weather",
subtitle="Type 'wth <city>' to get current weather",
icon_name="weather-clear-symbolic",
score=100.0,
render_type="default",
)
]
data = self._fetch_weather(q, token)
if token.is_cancelled or not data:
return []
temp = data.get("main", {}).get("temp", "?")
desc = data.get("weather", [{}])[0].get("description", "?")
city = data.get("name", q)
return [
SearchResult(
id=f"weather_{q}",
title=f"{city}: {temp}°C",
subtitle=desc.capitalize(),
icon_name="weather-clear-symbolic",
score=100.0,
render_type="default",
)
]
def _fetch_weather(self, city: str, token: Any) -> dict | None:
if self._cache:
return self._cache
api_key = os.environ.get("OPENWEATHER_API_KEY")
if not api_key or requests is None:
return None
try:
resp = requests.get(
"https://api.openweathermap.org/data/2.5/weather",
params={"q": city, "appid": api_key, "units": "metric"},
timeout=10,
)
if token.is_cancelled:
return None
if resp.status_code == 200:
self._cache = resp.json()
return self._cache
except Exception:
pass
return None
def handle_external(self, command: str, args: str) -> None:
print(f"[Weather] External: {command} {args}")
def on_activate(self, update_fn=None) -> None:
print("[Weather] Keyword mode activated")
def on_deactivate(self) -> None:
print("[Weather] Keyword mode deactivated")
PLUGIN = WeatherPlugin