Source code for animaid.window

"""Window configuration and runtime control for AnimAID applications."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
    from animaid.animate import App


[docs] @dataclass class WindowConfig: """Configuration for window initialization. Use this to configure the initial state of the browser window when creating an App. Examples: >>> from animaid import App, WindowConfig >>> # Use a preset for quick setup >>> config = WindowConfig.compact(title="My Tool") >>> with App(window=config) as app: ... app.add(HTMLString("Hello")) >>> # Or configure manually >>> config = WindowConfig( ... title="Dashboard", ... width=1280, ... height=720, ... theme="dark" ... ) """ title: str = "AnimAID" width: int | None = None # None = browser default height: int | None = None # None = browser default theme: str = "light" # "light", "dark", "auto" background_color: str = "#fafafa" favicon: str | None = None
[docs] @classmethod def compact(cls, title: str = "AnimAID") -> WindowConfig: """Create a compact window configuration (600x400). Args: title: The window title. Returns: A WindowConfig for a small, compact window. """ return cls(title=title, width=600, height=400)
[docs] @classmethod def standard(cls, title: str = "AnimAID") -> WindowConfig: """Create a standard window configuration (1024x768). Args: title: The window title. Returns: A WindowConfig for a standard-sized window. """ return cls(title=title, width=1024, height=768)
[docs] @classmethod def wide(cls, title: str = "AnimAID") -> WindowConfig: """Create a wide window configuration (1280x720). Args: title: The window title. Returns: A WindowConfig for a wide (16:9) window. """ return cls(title=title, width=1280, height=720)
[docs] @classmethod def dark(cls, title: str = "AnimAID") -> WindowConfig: """Create a dark-themed window configuration. Args: title: The window title. Returns: A WindowConfig with dark theme. """ return cls(title=title, theme="dark", background_color="#1a1a2e")
[docs] class Window: """Runtime window control for AnimAID applications. Access this via the `app.window` property. Provides methods to dynamically control the browser window appearance and behavior. Examples: >>> with App() as app: ... app.window.dark() # Switch to dark theme ... app.window.set_title("Processing...") ... app.window.resize(1280, 720) """ def __init__(self, app: App, config: WindowConfig) -> None: """Initialize the Window with configuration. Args: app: The parent App instance. config: Initial window configuration. """ self._app = app self._title = config.title self._width = config.width self._height = config.height self._theme = config.theme self._background_color = config.background_color self._favicon = config.favicon self._on_resize_callback: Callable[[int, int], None] | None = None self._on_close_callback: Callable[[], None] | None = None # Read-only properties @property def title(self) -> str: """Get the current window title.""" return self._title @property def width(self) -> int | None: """Get the current window width (None if browser default).""" return self._width @property def height(self) -> int | None: """Get the current window height (None if browser default).""" return self._height @property def theme(self) -> str: """Get the current theme ('light', 'dark', or 'auto').""" return self._theme @property def background_color(self) -> str: """Get the current background color.""" return self._background_color # Mutators (broadcast to browser)
[docs] def set_title(self, title: str) -> Window: """Set the window title. Updates the browser tab title in real-time. Args: title: The new window title. Returns: Self for method chaining. Examples: >>> app.window.set_title("Processing... 50%") """ self._title = title self._broadcast_window_change("title", title) return self
[docs] def resize(self, width: int, height: int) -> Window: """Resize the browser window. Note: Some browsers may restrict window resizing for security. Args: width: The new width in pixels. height: The new height in pixels. Returns: Self for method chaining. Examples: >>> app.window.resize(1280, 720) """ self._width = width self._height = height self._broadcast_window_change("resize", {"width": width, "height": height}) return self
[docs] def set_theme(self, theme: str) -> Window: """Set the color theme. Args: theme: The theme to use ('light', 'dark', or 'auto'). Returns: Self for method chaining. Examples: >>> app.window.set_theme("dark") """ if theme not in ("light", "dark", "auto"): raise ValueError(f"Invalid theme: {theme}. Must be 'light', 'dark', or 'auto'.") self._theme = theme self._broadcast_window_change("theme", theme) return self
[docs] def set_background(self, color: str) -> Window: """Set the page background color. Args: color: CSS color value (e.g., '#ffffff', 'rgb(0,0,0)', 'white'). Returns: Self for method chaining. Examples: >>> app.window.set_background("#1a1a2e") """ self._background_color = color self._broadcast_window_change("background", color) return self
[docs] def set_favicon(self, url: str) -> Window: """Set the page favicon. Args: url: URL to the favicon image. Returns: Self for method chaining. Examples: >>> app.window.set_favicon("/static/icon.png") """ self._favicon = url self._broadcast_window_change("favicon", url) return self
# Preset methods
[docs] def dark(self) -> Window: """Switch to dark theme. Convenience method equivalent to `set_theme("dark")`. Returns: Self for method chaining. Examples: >>> app.window.dark() """ return self.set_theme("dark")
[docs] def light(self) -> Window: """Switch to light theme. Convenience method equivalent to `set_theme("light")`. Returns: Self for method chaining. Examples: >>> app.window.light() """ return self.set_theme("light")
[docs] def fullscreen(self) -> Window: """Request fullscreen mode. Note: Browsers may require user interaction before allowing fullscreen. Returns: Self for method chaining. Examples: >>> app.window.fullscreen() """ self._broadcast_window_change("fullscreen", True) return self
# Event callbacks
[docs] def on_resize(self, callback: Callable[[int, int], None]) -> Window: """Register a callback for window resize events. Args: callback: Function that receives (width, height) when window resizes. Returns: Self for method chaining. Examples: >>> def handle_resize(width, height): ... print(f"Window resized to {width}x{height}") >>> app.window.on_resize(handle_resize) """ self._on_resize_callback = callback return self
[docs] def on_close(self, callback: Callable[[], None]) -> Window: """Register a callback for window close events. Args: callback: Function called when the window is about to close. Returns: Self for method chaining. Examples: >>> def handle_close(): ... print("Window closing!") >>> app.window.on_close(handle_close) """ self._on_close_callback = callback return self
[docs] def handle_window_event(self, event: str, data: dict[str, Any]) -> None: """Handle a window event from the browser. This is called internally by the server. Args: event: The event type ('resize', 'close'). data: Event-specific data. """ if event == "resize": width = data.get("width", 0) height = data.get("height", 0) self._width = width self._height = height if self._on_resize_callback: self._on_resize_callback(width, height) elif event == "close": if self._on_close_callback: self._on_close_callback()
def _broadcast_window_change(self, property_name: str, value: Any) -> None: """Broadcast a window property change to all connected clients. Args: property_name: The property that changed. value: The new value. """ self._app._broadcast({"type": "window", "property": property_name, "value": value})
[docs] def get_config(self) -> dict[str, Any]: """Get the current window configuration as a dictionary. Used by the server to send initial configuration to clients. Returns: Dictionary with current window settings. """ return { "title": self._title, "width": self._width, "height": self._height, "theme": self._theme, "background_color": self._background_color, "favicon": self._favicon, }