Coverage for src / lexigram / ui / di / provider.py: 94%
36 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-10 04:11 +0800
1"""UIProvider — registers the UI component system in the DI container."""
3from __future__ import annotations
5import time
6from typing import TYPE_CHECKING, Any
8from lexigram.contracts.core import HealthCheckResult, HealthStatus, ProviderPriority
9from lexigram.di.provider import Provider
10from lexigram.ui.config import (
11 BaseLayoutConfig,
12 FooterConfig,
13 HeadConfig,
14 HTMLDocumentConfig,
15 ToastConfig,
16 UIConfig,
17)
19if TYPE_CHECKING:
20 from lexigram.contracts.core.di import (
21 ContainerRegistrarProtocol,
22 ContainerResolverProtocol,
23 )
26class UIProvider(Provider):
27 """Registers the lexigram-ui component system.
29 Bind into your application bootstrap::
31 from lexigram.ui.di import UIProvider
33 app.add_provider(UIProvider())
35 Configuration (``application.yaml``)::
37 ui:
38 default_theme: my-theme
39 debug_components: true
41 Registered services:
43 - ``UIConfig`` (singleton) — resolved UI configuration.
44 - ``MetricsCollector`` (singleton) — in-memory UI metrics collection.
45 - ``ResponseOptimizer`` (singleton) — ETag-based HTMX response optimization.
46 - ``RenderCache`` (singleton) — LRU cache for rendered component fragments.
47 """
49 name = "ui"
50 priority = ProviderPriority.PRESENTATION
51 config_key: str | None = "ui"
52 config_model: type | None = UIConfig
54 def __init__(self, config: UIConfig | None = None, **kwargs: Any) -> None:
55 super().__init__(**kwargs)
56 self._requested_config = config
57 if config is not None:
58 self._config = config
60 async def register(self, container: ContainerRegistrarProtocol) -> None:
61 """Register UI services in the DI container.
63 Args:
64 container: The DI container registrar.
65 """
66 self._config = self._requested_config or (
67 self.config
68 if isinstance(getattr(self, "config", None), UIConfig)
69 else self._config
70 )
71 config: UIConfig = (
72 self._config if isinstance(self._config, UIConfig) else UIConfig()
73 )
74 container.singleton(UIConfig, config)
76 # Layout config singletons
77 container.singleton(HTMLDocumentConfig, HTMLDocumentConfig())
78 container.singleton(BaseLayoutConfig, BaseLayoutConfig())
79 container.singleton(HeadConfig, HeadConfig())
80 container.singleton(FooterConfig, FooterConfig())
81 container.singleton(ToastConfig, ToastConfig())
83 # Monitoring services — explicitly registered for DI traceability
84 from lexigram.ui.performance.observability import MetricsCollector
85 from lexigram.ui.performance.performance import RenderCache, ResponseOptimizer
87 container.singleton(MetricsCollector, MetricsCollector)
88 container.singleton(ResponseOptimizer, ResponseOptimizer)
89 container.singleton(RenderCache, RenderCache)
91 async def boot(self, container: ContainerResolverProtocol) -> None:
92 """No boot-time initialisation required for the UI module."""
94 async def shutdown(self) -> None:
95 """No shutdown work required for the UI module."""
97 async def health_check(self, timeout: float = 5.0) -> HealthCheckResult:
98 """Check provider health.
100 Args:
101 timeout: Maximum seconds to wait for health check response.
103 Returns:
104 HealthCheckResult with status and component details.
105 """
106 start = time.perf_counter()
107 return HealthCheckResult(
108 component="ui",
109 status=HealthStatus.HEALTHY,
110 details={"components": {"ui": {"status": "healthy"}}},
111 duration_ms=(time.perf_counter() - start) * 1000,
112 )
115__all__ = ["UIProvider"]