Coverage for src / lexigram / admin / controllers / command_palette.py: 50%

28 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-08-13 22:14 +0800

1"""Controller for the command palette endpoint.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from starlette.requests import Request 

8from starlette.responses import JSONResponse 

9 

10from lexigram.admin.services.search_service import SearchService 

11from lexigram.di.decorators import inject 

12from lexigram.logging import get_logger 

13 

14logger = get_logger(__name__) 

15 

16_STATIC_COMMANDS: list[dict[str, Any]] = [ 

17 {"label": "Go to Dashboard", "href": "/admin/", "icon": "home", "shortcut": "G D"}, 

18 { 

19 "label": "Manage Users", 

20 "href": "/admin/users", 

21 "icon": "users", 

22 "shortcut": "G U", 

23 }, 

24 { 

25 "label": "Toggle Dark Mode", 

26 "action": "darkMode = !darkMode", 

27 "icon": "moon", 

28 "shortcut": "T D", 

29 }, 

30 {"label": "Settings", "href": "#", "icon": "settings", "shortcut": ","}, 

31] 

32 

33_MIN_QUERY_LENGTH = 2 

34 

35 

36@inject 

37class CommandPaletteController: 

38 """Handles the command palette search endpoint. 

39 

40 Returns JSON commands that the frontend merges with static commands. 

41 """ 

42 

43 def __init__(self, search_service: SearchService) -> None: 

44 self._search_service = search_service 

45 

46 async def search(self, request: Request) -> JSONResponse: 

47 """Handle GET /admin/command-palette?q=...""" 

48 query = (request.query_params.get("q") or "").strip() 

49 commands: list[dict[str, Any]] = [] 

50 

51 # Filter static commands by query 

52 for cmd in _STATIC_COMMANDS: 

53 if not query or query.lower() in cmd["label"].lower(): 

54 commands.append(cmd) 

55 

56 # Dynamic search results from backend 

57 if len(query) >= _MIN_QUERY_LENGTH: 

58 try: 

59 results = await self._search_service.search(query) 

60 for r in results.results: 

61 commands.append( 

62 { 

63 "label": f"{r.resource_label}: {r.title}", 

64 "href": r.url, 

65 "icon": "search", 

66 "subtitle": r.subtitle, 

67 } 

68 ) 

69 except Exception: 

70 logger.exception("Command palette search failed for query=%s", query) 

71 

72 return JSONResponse(commands)