Coverage for src / lexigram / ui / config.py: 100%
102 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"""UIConfig — configuration model for the UI provider."""
3from __future__ import annotations
5from dataclasses import dataclass, field
6from typing import Any, ClassVar
8from lexigram.config import BaseConfig
9from lexigram.contracts.core.config import ConfigIssue, Environment
10from lexigram.ui.constants import ENV_NESTED_DELIMITER, ENV_PREFIX
11from lexigram.validation import ConfigDict, Field
14@dataclass
15class DebounceConfig:
16 """Configuration for debounced HTMX triggers."""
18 delay_ms: int = 300
19 changed: bool = True
21 def to_trigger(self, base_trigger: str = "input") -> str:
22 """Generate HTMX trigger string with debounce."""
23 parts = [base_trigger]
24 if self.changed:
25 parts.append("changed")
26 parts.append(f"delay:{self.delay_ms}ms")
27 return " ".join(parts)
30@dataclass(init=False)
31class UIConfig(BaseConfig):
32 """Configuration for the lexigram-ui provider.
34 These settings control rendering defaults and are read from the
35 ``[ui]`` section of ``application.yaml`` (or equivalent config source).
36 """
38 config_section: ClassVar[str] = "ui"
40 model_config: ClassVar[ConfigDict] = ConfigDict(
41 env_prefix=ENV_PREFIX, # type: ignore[typeddict-unknown-key]
42 env_nested_delimiter=ENV_NESTED_DELIMITER,
43 extra="ignore",
44 )
46 default_theme: str = Field(default="default", description="Default CSS theme name.")
47 auto_escape: bool = Field(
48 default=True, description="HTML-escape user strings by default."
49 )
50 htmx_version: str = Field(default="2.0.4", description="HTMX CDN version.")
51 debug_components: bool = Field(
52 default=False, description="Render data-component debug attributes."
53 )
54 theme: str = Field(default="light", description="Active UI theme.")
55 enable_sse: bool = Field(
56 default=False, description="Enable Server-Sent Events support."
57 )
58 enable_realtime: bool = Field(
59 default=False, description="Enable realtime update features."
60 )
62 def validate_for_environment(
63 self, env: Environment | None = None
64 ) -> list[ConfigIssue]:
65 """Check config is safe for the target environment."""
66 issues: list[ConfigIssue] = []
67 if env == Environment.PRODUCTION:
68 if self.debug_components:
69 issues.append(
70 ConfigIssue(
71 severity="warning",
72 field="debug_components",
73 message="Component debugging enabled in production",
74 suggestion="Set debug_components: false for performance",
75 )
76 )
77 return issues
80@dataclass
81class HTMLDocumentConfig:
82 """Configuration for HTML document generation."""
84 # Document basics
85 lang: str = "en"
86 charset: str = "UTF-8"
88 # Meta tags
89 viewport: str = "width=device-width, initial-scale=1.0"
90 description: str = ""
91 keywords: list[str] = field(default_factory=list)
92 author: str = ""
93 robots: str = "" # e.g., "noindex, nofollow"
95 # Favicon
96 favicon: str | None = None
97 favicon_type: str = "image/x-icon"
99 # Theme
100 theme_color: str = "#ffffff"
102 # Open Graph
103 og_title: str = ""
104 og_description: str = ""
105 og_image: str = ""
106 og_url: str = ""
107 og_type: str = "website"
109 # Additional head content
110 extra_head: str = ""
113@dataclass
114class BaseLayoutConfig(HTMLDocumentConfig):
115 """Base configuration for all layouts.
117 Extends HTMLDocumentConfig with common layout settings.
118 """
120 # Branding
121 site_name: str = "Lexigram Admin"
122 site_logo: str | None = None
123 site_logo_alt: str = "Logo"
125 # Theme
126 theme: str = "light"
127 primary_color: str = "#6b7280"
128 accent_color: str = "#8b5cf6"
130 # HTMX
131 htmx_enabled: bool = True
132 htmx_boost: bool = True
133 htmx_version: str = "1.9.10"
135 # External resources
136 css_files: list[str] = field(default_factory=list)
137 js_files: list[str] = field(default_factory=list)
139 # Features
140 include_alpine: bool = False
141 alpine_version: str = "3.x.x"
144@dataclass
145class HeadConfig:
146 """Configuration for head section."""
148 # Default CSS framework
149 css_framework: str = "tailwind" # tailwind, pico, custom
150 css_framework_url: str = ""
152 # Custom CSS
153 css_files: list[str] = field(default_factory=list)
154 inline_css: str = ""
156 # Icons
157 icon_library: str = "lucide" # lucide, heroicons, feather
158 icon_library_url: str = "https://unpkg.com/lucide@0.263.1/dist/umd/lucide.min.js"
160 # Fonts
161 font_url: str = ""
163 # HTMX
164 htmx_url: str = "https://unpkg.com/htmx.org@1.9.10"
165 include_hyperscript: bool = False
166 hyperscript_url: str = "https://unpkg.com/hyperscript.org@0.9.12"
169@dataclass
170class FooterConfig:
171 """Configuration for footer."""
173 # Copyright
174 show_copyright: bool = True
175 copyright_holder: str = ""
176 copyright_start_year: int | None = None
178 # Version
179 show_version: bool = True
180 version: str = ""
182 # Links
183 links: list[Any] = field(default_factory=list)
185 # Styling
186 sticky: bool = False
187 show_divider: bool = True
189 # Custom content
190 custom_left: str = ""
191 custom_right: str = ""
194@dataclass
195class ToastConfig:
196 """Configuration for toast container."""
198 # Position
199 position: str = "top-right" # top-right, top-left, bottom-right, bottom-left
201 # Behavior
202 default_duration_ms: int = 5000
203 max_toasts: int = 5
204 stack_direction: str = "down" # up or down
206 # HTMX
207 listen_for_events: bool = True
208 event_name: str = "showToast"
210 # Animation
211 animation_in: str = "fade-in-right"
212 animation_out: str = "fade-out-right"
215__all__ = [
216 "BaseLayoutConfig",
217 "DebounceConfig",
218 "FooterConfig",
219 "HTMLDocumentConfig",
220 "HeadConfig",
221 "ToastConfig",
222 "UIConfig",
223]