Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/aignostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

os.environ["LOGFIRE_PYDANTIC_RECORD"] = "off"

from aignostics.constants import SENTRY_INTEGRATIONS # noqa: E402
from aignostics.constants import SENTRY_INTEGRATIONS, WINDOW_TITLE # noqa: E402
from aignostics.utils import boot, gui_run # noqa: E402

boot(SENTRY_INTEGRATIONS)
Expand Down Expand Up @@ -80,4 +80,4 @@
else:
if pyi_splash and pyi_splash.is_alive():
pyi_splash.update_text("Opening user interface ...")
gui_run(native=True, with_api=False, title="Aignostics Launchpad", icon="🔬")
gui_run(native=True, with_api=False, title=WINDOW_TITLE, icon="🔬")
4 changes: 2 additions & 2 deletions src/aignostics/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import typer
from loguru import logger

from .constants import NOTEBOOK_DEFAULT
from .constants import NOTEBOOK_DEFAULT, WINDOW_TITLE
from .utils import (
__is_running_in_container__,
__python_version__,
Expand All @@ -27,7 +27,7 @@ def launchpad() -> None:
"""Open Aignostics Launchpad, the graphical user interface of the Aignostics Platform."""
from .utils import gui_run # noqa: PLC0415

gui_run(native=True, with_api=False, title="Aignostics Launchpad", icon="🔬")
gui_run(native=True, with_api=False, title=WINDOW_TITLE, icon="🔬")


if find_spec("marimo"):
Expand Down
2 changes: 2 additions & 0 deletions src/aignostics/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@
TEST_APP_APPLICATION_ID = "test-app"
WSI_SUPPORTED_FILE_EXTENSIONS = {".dcm", ".tiff", ".tif", ".svs"}
WSI_SUPPORTED_FILE_EXTENSIONS_TEST_APP = {".tiff"}

WINDOW_TITLE = "Aignostics Launchpad"
34 changes: 34 additions & 0 deletions src/aignostics/gui/_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@

from html_sanitizer import Sanitizer
from humanize import naturaldelta
from loguru import logger

from aignostics.constants import WINDOW_TITLE
from aignostics.utils import __version__, open_user_data_directory

from ._theme import theme
Expand Down Expand Up @@ -50,6 +52,34 @@ def frame( # noqa: C901, PLR0915

theme()

def _bring_window_to_front() -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Excellent Documentation:

The docstring clearly explains:

  • What the function does
  • Why it's needed (pywebview limitations on Windows)
  • How it works on different platforms

This is exactly the level of documentation expected for medical device software. Well done!

Minor Enhancement Suggestion:
Consider adding a note about when this is called (after OAuth completes) to make the user flow clearer:

"""Bring the native window to front after authentication completes.

Called after OAuth device flow completes in the browser to return
focus to the Launchpad window, improving user experience.

Uses platform-specific approaches:
...

"""Bring the native window to front after authentication completes.

Uses platform-specific approaches:
- Windows: Uses ctypes to find window by title and call SetForegroundWindow,
as pywebview's set_always_on_top/show methods don't reliably bring windows
to front and the window handle isn't directly exposed.
- macOS/Linux: Uses pywebview's built-in methods.
"""
if not app.native.main_window:
return
try:
if platform.system() == "Windows":
import ctypes # noqa: PLC0415

# Find window by title since pywebview doesn't expose hwnd directly
# FindWindowW(lpClassName, lpWindowName) - use None for class to match any
hwnd = ctypes.windll.user32.FindWindowW(None, WINDOW_TITLE) # type: ignore
if hwnd:
ctypes.windll.user32.SetForegroundWindow(hwnd) # type: ignore
else:
app.native.main_window.set_always_on_top(True)
app.native.main_window.show()
app.native.main_window.set_always_on_top(False)
except Exception as e:
logger.exception(f"Failed to bring window to front: {e}")
# Window operations can fail on some platforms

user_info: UserInfo | None = None
launchpad_healthy: bool | None = None

Expand Down Expand Up @@ -93,6 +123,8 @@ async def _user_info_ui_load() -> None:
await ui.context.client.connected()
app.storage.tab["user_info"] = user_info
_user_info_ui.refresh()
if user_info:
_bring_window_to_front()

ui.timer(interval=USERINFO_UPDATE_INTERVAL, callback=_user_info_ui_load, immediate=True)

Expand All @@ -105,6 +137,8 @@ async def _user_info_ui_relogin() -> None:
_user_info_ui.refresh()
with contextlib.suppress(Exception):
user_info = await run.io_bound(PlatformService.get_user_info, relogin=True)
if user_info:
_bring_window_to_front()
app.storage.tab["user_info"] = user_info
ui.navigate.reload()

Expand Down
3 changes: 2 additions & 1 deletion tests/aignostics/cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from typer.testing import CliRunner

from aignostics.cli import cli
from aignostics.constants import WINDOW_TITLE
from aignostics.utils import (
__python_version__,
__version__,
Expand Down Expand Up @@ -205,7 +206,7 @@ def mock_app_mount(path, app_instance):

# Check that ui.run was called with the expected parameters
assert mock_ui_run_called, "ui.run was not called"
assert mock_ui_run_args["title"] == "Aignostics Launchpad", "title parameter is incorrect"
assert mock_ui_run_args["title"] == WINDOW_TITLE, "title parameter is incorrect"
assert mock_ui_run_args["favicon"] == "🔬", "favicon parameter is incorrect"
if platform.system() == "Linux":
assert mock_ui_run_args["native"] is False, "native parameter should be False on Linux"
Expand Down
3 changes: 2 additions & 1 deletion tests/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"""Start script for pytest."""

from aignostics.constants import WINDOW_TITLE
from aignostics.utils import (
gui_run,
)

gui_run(native=False, with_api=False, title="Aignostics Launchpad", icon="🔬")
gui_run(native=False, with_api=False, title=WINDOW_TITLE, icon="🔬")
Loading