| """ |
| DungeonMaster AI - Voice Controls Component |
| |
| Voice narration settings and controls. |
| """ |
|
|
| from __future__ import annotations |
|
|
| from typing import TYPE_CHECKING |
|
|
| import gradio as gr |
|
|
| from ui import styles |
|
|
| if TYPE_CHECKING: |
| pass |
|
|
|
|
| |
| |
| |
|
|
|
|
| def create_voice_controls() -> tuple[ |
| gr.Checkbox, |
| gr.Checkbox, |
| gr.HTML, |
| ]: |
| """ |
| Create the voice controls component. |
| |
| Returns: |
| Tuple of all Gradio components for event wiring. |
| """ |
| with gr.Group(): |
| gr.Markdown("### 🔊 Voice Settings") |
|
|
| |
| voice_toggle = gr.Checkbox( |
| label="Enable Voice Narration", |
| value=True, |
| info="AI voice will narrate DM responses", |
| ) |
|
|
| |
| autoplay_toggle = gr.Checkbox( |
| label="Auto-play Narration", |
| value=True, |
| info="Automatically play audio when available", |
| ) |
|
|
| |
| voice_status = gr.HTML( |
| value=format_voice_status(available=True, enabled=True), |
| ) |
|
|
| return ( |
| voice_toggle, |
| autoplay_toggle, |
| voice_status, |
| ) |
|
|
|
|
| |
| |
| |
|
|
|
|
| def format_voice_status( |
| available: bool = True, |
| enabled: bool = True, |
| provider: str = "ElevenLabs", |
| ) -> str: |
| """Format voice status display as styled HTML.""" |
| container_style = ( |
| "display: flex; " |
| "align-items: center; " |
| "gap: 8px; " |
| "padding: 0.5rem; " |
| "background: rgba(0, 0, 0, 0.15); " |
| "border-radius: 6px; " |
| "margin-top: 0.5rem;" |
| ) |
|
|
| text_style = f"font-size: 0.85rem; color: {styles.PARCHMENT};" |
| muted_style = f"font-size: 0.8rem; color: {styles.PARCHMENT_MUTED}; margin-top: 0.25rem;" |
|
|
| if not available: |
| return f''' |
| <div style="{container_style}"> |
| <span style="{styles.status_dot_style(styles.STATUS_RED)}"></span> |
| <div> |
| <div style="{text_style}; color: {styles.STATUS_RED};">Voice Unavailable</div> |
| <div style="{muted_style}">Text responses will continue normally.</div> |
| </div> |
| </div> |
| ''' |
|
|
| if not enabled: |
| return f''' |
| <div style="{container_style}"> |
| <span style="{styles.status_dot_style(styles.STATUS_YELLOW)}"></span> |
| <div> |
| <div style="{text_style}; color: {styles.STATUS_YELLOW};">Voice Disabled</div> |
| <div style="{muted_style}">Enable above to hear the DM speak!</div> |
| </div> |
| </div> |
| ''' |
|
|
| return f''' |
| <div style="{container_style}"> |
| <span style="{styles.status_dot_style(styles.STATUS_GREEN)}"></span> |
| <div> |
| <div style="{text_style}; color: {styles.STATUS_GREEN};">Voice Active</div> |
| <div style="{muted_style}">Provider: {provider}</div> |
| </div> |
| </div> |
| ''' |
|
|
|
|
| |
| |
| |
|
|
|
|
| def update_voice_status( |
| available: bool, |
| enabled: bool, |
| provider: str = "ElevenLabs", |
| ) -> str: |
| """Update voice status display.""" |
| return format_voice_status( |
| available=available, |
| enabled=enabled, |
| provider=provider, |
| ) |
|
|
|
|
| def handle_voice_toggle(enabled: bool) -> tuple[bool, str]: |
| """ |
| Handle voice toggle change. |
| |
| Args: |
| enabled: New toggle state |
| |
| Returns: |
| Tuple of (enabled_state, status_text) |
| """ |
| return enabled, format_voice_status(available=True, enabled=enabled) |
|
|
|
|
| def handle_autoplay_toggle(enabled: bool) -> gr.update: |
| """ |
| Handle autoplay toggle change. |
| |
| Args: |
| enabled: New toggle state |
| |
| Returns: |
| Audio component update with autoplay setting |
| """ |
| return gr.update(autoplay=enabled) |
|
|