import asyncio from nicegui import ui, app from journal.core.config import ( SPEECH_RECOGNITION_ENGINE, WHISPER_MODEL_SIZE, SERVER_CONTROL_FILE, ) from typing import cast def settings_dialog(): """Creates a dialog for application settings.""" async def request_server_action(action: str) -> None: try: SERVER_CONTROL_FILE.parent.mkdir(parents=True, exist_ok=True) SERVER_CONTROL_FILE.write_text(action, encoding="utf-8") except Exception as error: ui.notify(f"Failed to queue server action: {error}", type="negative") return if action == "restart": ui.notify("Server restart requested...", type="warning") else: ui.notify("Server shutdown requested...", type="warning") # Give the notification a moment to flush before shutdown. await asyncio.sleep(0.2) app.shutdown() async def restart_server() -> None: await request_server_action("restart") async def shutdown_server() -> None: await request_server_action("shutdown") with ui.dialog() as dialog, ui.card().classes("w-80"): _ = ui.label("Settings").classes("text-xl font-bold") _ = ui.label("Speech to Text").classes("text-lg font-medium mt-4") if "speech_engine" in app.storage.user: initial_engine_value = cast(str, app.storage.user["speech_engine"]) else: initial_engine_value = SPEECH_RECOGNITION_ENGINE _ = ( ui.select( ["whisper", "google", "sphinx"], label="Recognition Engine", value=initial_engine_value, on_change=lambda e: ui.notify( f"Engine set to {cast(str, e.value)}. Takes effect on next recording." ), ) .bind_value(app.storage.user, "speech_engine") .classes("w-full") ) _ = ui.markdown( "`whisper` is local & private (recommended).\n`google` is online.\n`sphinx` is offline & fast." ).classes("text-xs text-gray-500") # Add whisper model select _ = ui.separator().classes("my-2") _ = ui.label("Whisper Model Size").classes("font-medium") if "whisper_model" in app.storage.user: initial_whisper_model = cast(str, app.storage.user["whisper_model"]) else: initial_whisper_model = WHISPER_MODEL_SIZE _ = ( ui.select( ["tiny", "base", "small", "medium", "large"], label="Accuracy vs. Speed", value=initial_whisper_model, on_change=lambda e: ui.notify( f"Whisper model set to {cast(str, e.value)}. Takes effect on next recording." ), ) .bind_value(app.storage.user, "whisper_model") .classes("w-full") ) _ = ui.markdown( "`base` is a good balance. Larger models are more accurate but slower." ).classes("text-xs text-gray-500") _ = ui.separator().classes("my-2") _ = ui.label("Server Controls").classes("font-medium") if cast(bool, app.storage.user.get("authenticated", False)): _ = ui.markdown( "Use these when running via `run_desktop.py`.\n" "`Restart` reconnects quickly, `Shutdown` stops the server wrapper." ).classes("text-xs text-gray-500") with ui.row().classes("w-full gap-2 mt-1"): _ = ( ui.button( "Restart Server", on_click=restart_server, ) .props("color=warning") .classes("flex-1") ) _ = ( ui.button( "Shutdown Server", on_click=shutdown_server, ) .props("color=negative") .classes("flex-1") ) else: _ = ui.label("Log in to enable restart/shutdown controls.").classes( "text-xs text-gray-500" ) _ = ( ui.button("Close", on_click=dialog.close) .props("flat") .classes("mt-4 self-end") ) return dialog