uxoxo/eb2ab
0
1"""2UI Helper Functions - Design System Integration3 4Helper functions to create consistent UI components using the design system5throughout the ebook2audiobook application.6 7Author: Claude Code8Date: 2025-10-219"""10 11import gradio as gr12from typing import Optional, List, Dict, Any, Callable13 14# Import design system components15try:16 from design_system import (17 button, card, alert, tag, progress_bar,18 status_indicator, estimate_box, spinner, tokens19 )20 HAS_DESIGN_SYSTEM = True21except ImportError:22 HAS_DESIGN_SYSTEM = False23 # Fallback to standard Gradio if design system not available24 print("Warning: Design system not available, using standard Gradio components")25 26 27def create_primary_button(label: str, icon: Optional[str] = None, **kwargs) -> gr.Button:28 """Create a primary action button with consistent styling"""29 if HAS_DESIGN_SYSTEM:30 return button(label, variant="primary", size="lg", icon=icon, **kwargs)31 else:32 display_label = f"{icon} {label}" if icon else label33 return gr.Button(display_label, variant="primary", size="lg", **kwargs)34 35 36def create_secondary_button(label: str, icon: Optional[str] = None, **kwargs) -> gr.Button:37 """Create a secondary action button"""38 if HAS_DESIGN_SYSTEM:39 return button(label, variant="secondary", icon=icon, **kwargs)40 else:41 display_label = f"{icon} {label}" if icon else label42 return gr.Button(display_label, variant="secondary", **kwargs)43 44 45def create_outline_button(label: str, icon: Optional[str] = None, **kwargs) -> gr.Button:46 """Create an outline/ghost button for less prominent actions"""47 if HAS_DESIGN_SYSTEM:48 return button(label, variant="outline", icon=icon, **kwargs)49 else:50 display_label = f"{icon} {label}" if icon else label51 return gr.Button(display_label, **kwargs)52 53 54def create_destructive_button(label: str, icon: Optional[str] = None, **kwargs) -> gr.Button:55 """Create a destructive action button (delete, reset, etc.)"""56 if HAS_DESIGN_SYSTEM:57 return button(label, variant="destructive", icon=icon, **kwargs)58 else:59 display_label = f"{icon} {label}" if icon else label60 return gr.Button(display_label, variant="stop", **kwargs)61 62 63def create_section_card(title: str, **kwargs):64 """65 Create a card container for grouping related components66 67 Usage:68 with create_section_card("Upload Settings"):69 gr.File(...)70 gr.Dropdown(...)71 """72 if HAS_DESIGN_SYSTEM:73 return card(title=title, **kwargs)74 else:75 # Fallback: Use Group with markdown title76 col = gr.Column(**kwargs)77 col.__enter__()78 gr.Markdown(f"### {title}")79 return col80 81 82def create_status_tag(text: str, state: str = "pending") -> gr.HTML:83 """84 Create a status tag/badge85 86 States: pending, processing, modernized, approved, error87 """88 if HAS_DESIGN_SYSTEM:89 return tag(text, state=state)90 else:91 # Fallback: colored markdown92 colors = {93 "pending": "#A3A3A3",94 "processing": "#F5A623",95 "approved": "#2ECC71",96 "error": "#E74C3C"97 }98 color = colors.get(state, "#A3A3A3")99 html = f'<span style="background: {color}20; color: {color}; padding: 4px 12px; border-radius: 12px; font-size: 14px; font-weight: 500;">{text}</span>'100 return gr.HTML(html)101 102 103def create_alert_box(message: str, variant: str = "info", icon: Optional[str] = None) -> gr.HTML:104 """105 Create an alert/notification box106 107 Variants: info, success, warning, error108 """109 if HAS_DESIGN_SYSTEM:110 return alert(message, variant=variant, icon=icon)111 else:112 # Fallback: colored markdown113 colors = {114 "info": {"bg": "#E0F2FE", "border": "#0EA5E9", "text": "#0284C7"},115 "success": {"bg": "#D1FAE5", "border": "#2ECC71", "text": "#059669"},116 "warning": {"bg": "#FEF3C7", "border": "#F5A623", "text": "#D97706"},117 "error": {"bg": "#FEE2E2", "border": "#E74C3C", "text": "#DC2626"}118 }119 style = colors.get(variant, colors["info"])120 121 icons_default = {122 "info": "ℹ️",123 "success": "✅",124 "warning": "⚠️",125 "error": "❌"126 }127 display_icon = icon or icons_default.get(variant, "")128 129 html = f'''130 <div style="131 background: {style['bg']};132 border-left: 4px solid {style['border']};133 color: {style['text']};134 padding: 16px;135 border-radius: 8px;136 margin: 10px 0;137 display: flex;138 align-items: flex-start;139 gap: 12px;140 ">141 <span style="font-size: 20px;">{display_icon}</span>142 <div>{message}</div>143 </div>144 '''145 return gr.HTML(html)146 147 148def create_progress_indicator(value: float = 0, state: str = "default", **kwargs) -> gr.Slider:149 """150 Create a progress bar151 152 States: default, processing, complete, error153 """154 if HAS_DESIGN_SYSTEM:155 return progress_bar(value=value, state=state, **kwargs)156 else:157 return gr.Slider(158 minimum=0,159 maximum=100,160 value=value,161 label="Progress",162 interactive=False,163 **kwargs164 )165 166 167def create_status_display(label: str, state: str, progress: Optional[float] = None) -> gr.Column:168 """169 Create a status indicator with optional progress bar170 171 Usage:172 status_display = create_status_display("15/20 Approved", "processing", 75)173 """174 if HAS_DESIGN_SYSTEM:175 return status_indicator(label=label, state=state, progress=progress)176 else:177 col = gr.Column()178 with col:179 with gr.Row():180 create_status_tag(label, state)181 if progress is not None:182 gr.Markdown(f"{progress:.0f}%")183 if progress is not None:184 create_progress_indicator(progress, state)185 return col186 187 188def create_cost_estimate_box(cost: str, time: str, details: Optional[str] = None) -> gr.Column:189 """190 Create an estimate display box for costs and time191 192 Usage:193 estimate = create_cost_estimate_box("$2.50", "~15 minutes", "Details...")194 """195 if HAS_DESIGN_SYSTEM:196 return estimate_box(cost=cost, time=time, details=details)197 else:198 col = gr.Column()199 with col:200 gr.Markdown(f"""201 <div style="background: linear-gradient(135deg, #E0F2FE 0%, #E8EDFF 100%);202 border: 2px solid #0EA5E9;203 border-radius: 16px;204 padding: 24px;205 margin: 16px 0;">206 <div style="font-size: 30px; font-weight: 700; color: #0284C7; margin-bottom: 8px;">207 {cost}208 </div>209 <div style="font-size: 20px; font-weight: 600; color: #059669;">210 {time}211 </div>212 {f'<div style="margin-top: 16px;">{details}</div>' if details else ''}213 </div>214 """)215 return col216 217 218def create_loading_spinner(size: int = 20) -> gr.HTML:219 """Create a loading spinner"""220 if HAS_DESIGN_SYSTEM:221 return spinner(size=size)222 else:223 html = f'''224 <div style="225 width: {size}px;226 height: {size}px;227 border: 3px solid #E5E5E5;228 border-top-color: #4A6FFF;229 border-radius: 50%;230 animation: spin 0.8s linear infinite;231 "></div>232 <style>233 @keyframes spin {{234 to {{ transform: rotate(360deg); }}235 }}236 </style>237 '''238 return gr.HTML(html)239 240 241def get_design_tokens():242 """243 Get design tokens for custom styling244 245 Returns dict with color, spacing, etc. tokens246 """247 if HAS_DESIGN_SYSTEM:248 return {249 'PRIMARY': tokens.PRIMARY,250 'SUCCESS': tokens.SUCCESS,251 'WARNING': tokens.WARNING,252 'ERROR': tokens.ERROR,253 'SPACE_2': tokens.SPACE_2,254 'SPACE_4': tokens.SPACE_4,255 'SPACE_6': tokens.SPACE_6,256 'RADIUS_LG': tokens.RADIUS_LG,257 'RADIUS_MD': tokens.RADIUS_MD,258 }259 else:260 return {261 'PRIMARY': '#4A6FFF',262 'SUCCESS': '#2ECC71',263 'WARNING': '#F5A623',264 'ERROR': '#E74C3C',265 'SPACE_2': '8px',266 'SPACE_4': '16px',267 'SPACE_6': '24px',268 'RADIUS_LG': '12px',269 'RADIUS_MD': '8px',270 }271 272 273def create_hero_section(title: str, subtitle: Optional[str] = None, icon: Optional[str] = None, **kwargs) -> gr.Markdown:274 """275 Create a hero section banner for tab headers276 277 Usage:278 create_hero_section(279 title="Convert Your Ebook to Audiobook",280 subtitle="Upload, configure, and generate high-quality narrated audiobooks",281 icon="📚"282 )283 """284 icon_display = f"{icon} " if icon else ""285 subtitle_display = f"\n\n{subtitle}" if subtitle else ""286 287 markdown_content = f"""288# {icon_display}{title}{subtitle_display}289"""290 return gr.Markdown(markdown_content, elem_id=kwargs.get('elem_id', 'hero-section'), elem_classes=['hero-section'])291 292 293def create_info_card(content: str, variant: str = "info", **kwargs) -> gr.Column:294 """295 Create an informational card (help text, tips, warnings)296 297 Variants: info, tip, warning298 299 Usage:300 with create_info_card("Voice cloning works best with 6-10 seconds of clear audio", variant="tip"):301 pass302 """303 icons = {304 "info": "ℹ️",305 "tip": "💡",306 "warning": "⚠️"307 }308 icon = icons.get(variant, "ℹ️")309 310 col = gr.Column(elem_classes=['info-card', f'info-card-{variant}'], **kwargs)311 col.__enter__()312 gr.Markdown(f"{icon} {content}")313 return col314 315 316def create_parameter_group(title: str, description: Optional[str] = None) -> gr.Group:317 """318 Create a visual group for related parameters within a card319 320 Usage:321 with create_parameter_group("Core Settings", "Adjust the main parameters"):322 gr.Slider(...)323 gr.Slider(...)324 """325 group = gr.Group(elem_classes=['parameter-group'])326 group.__enter__()327 if title:328 gr.Markdown(f"**{title}**", elem_classes=['parameter-group-title'])329 if description:330 gr.Markdown(f"*{description}*", elem_classes=['parameter-group-description'])331 return group332 333 334def create_action_row(**kwargs) -> gr.Row:335 """336 Create a standardized action row for buttons337 338 Usage:339 with create_action_row():340 reset_btn = create_destructive_button("Reset to Defaults", icon="↺")341 apply_btn = create_primary_button("Apply Settings", icon="✓")342 """343 return gr.Row(elem_classes=['action-button-row'], **kwargs)344 345 346def create_two_column_layout(**kwargs) -> gr.Row:347 """348 Create a responsive two-column layout349 350 Usage:351 with create_two_column_layout():352 with gr.Column(scale=1):353 # Left content354 with gr.Column(scale=1):355 # Right content356 """357 return gr.Row(elem_classes=['two-column-layout'], **kwargs)358 359 360# Convenience exports361__all__ = [362 'create_primary_button',363 'create_secondary_button',364 'create_outline_button',365 'create_destructive_button',366 'create_section_card',367 'create_status_tag',368 'create_alert_box',369 'create_progress_indicator',370 'create_status_display',371 'create_cost_estimate_box',372 'create_loading_spinner',373 'get_design_tokens',374 'create_hero_section',375 'create_info_card',376 'create_parameter_group',377 'create_action_row',378 'create_two_column_layout',379]380 