Skip to content

Document Rendering Module

WebEngine-based HTML document rendering system with theme support and automatic widget updates.

Quick Start

# At the very start of main.py (BEFORE QApplication)
from src.shared_services.rendering.documents.api import configure_chromium_flags
configure_chromium_flags()

# After QApplication is created
from src.shared_services.rendering.documents.api import (
    configure_default_profile,
    create_html_view,
)

# Configure profile once
configure_default_profile()

# Create views as needed
view = create_html_view(parent=self)
view.set_html("<h1>Willkommen</h1><p>Some content here.</p>")

When to Use What

Use Case What to Use
Theme-aware HTML display create_html_view()
External/unthemed content create_html_view_minimal()
Render with custom CSS view.set_html(html, custom_css="...")
Raw HTML (no theme) view.set_html_raw(html)
Reduce CPU when hidden view.freeze() / view.unfreeze()

HtmlView Widget

Theme-aware WebEngine view for displaying styled HTML content.

from src.shared_services.rendering.documents.api import create_html_view

# Basic usage
view = create_html_view(parent=self, object_name="NewsView")
view.set_html("<h1>Titel</h1><p>Absatz mit Text.</p>")

# With custom CSS
view.set_html(
    "<table><tr><td>Zelle</td></tr></table>",
    custom_css="table { margin: 20px; }"
)

# Raw HTML without theme styles
view.set_html_raw(complete_html_document)

# Lifecycle management
view.freeze()    # Reduce CPU when hidden
view.unfreeze()  # Restore when visible

Theme Integration

Views automatically update when the application theme changes.

# Register for automatic theme updates (default)
view.set_html(html, register_for_theme=True)

# Skip theme registration (for self-styled content)
view.set_html(html, register_for_theme=False)

# Manual theme control
from src.shared_services.rendering.documents.api import set_theme, get_theme
set_theme("dark")
current = get_theme()  # "light" or "dark"

Profile Configuration

WebEngine profile settings for desktop app usage.

from src.shared_services.rendering.documents.api import (
    configure_chromium_flags,
    configure_default_profile,
)

# MUST be called before QApplication
configure_chromium_flags()

# Call once after QApplication
configure_default_profile()

Profile optimizations: - No persistent cookies - No HTTP cache - Spellcheck disabled - Chromium flags for reduced memory usage

View Settings

Fine-tune WebEngine settings for specific use cases.

from src.shared_services.rendering.documents.api import (
    apply_base_optimizations,
    apply_full_optimizations,
    enable_javascript,
    enable_scrollbars,
)

# Base optimizations (safe for all content)
apply_base_optimizations(view)

# Full optimizations (for simple, known content)
apply_full_optimizations(view)

# Toggle features
enable_javascript(view, enabled=False)
enable_scrollbars(view, enabled=True)

Fun Facts

Daily rotating facts injected at placeholder locations.

from src.shared_services.rendering.documents.fun_facts import inject_fun_fact

# HTML with placeholder
html = """
<h1>Welcome</h1>
<p>{{FUN_FACT}}</p>
"""

html = inject_fun_fact(html)
# {{FUN_FACT}} replaced with "Honey never spoils..."

Files

File Purpose
api.py Public API exports
html_view.py HtmlView and HtmlViewMinimal widgets
profile_config.py Chromium flags and profile settings
view_settings.py WebEngine optimization settings
web_view_colors.py Theme-aware CSS generation
web_view_registry.py Widget tracking for theme updates
fun_facts.py Daily rotating facts
constants/alert_types.py Alert type definitions
constants/document_styles.py Typography constants

Architecture

Application Startup
    |
    v
configure_chromium_flags()  <-- BEFORE QApplication
    |
    v
QApplication created
    |
    v
configure_default_profile()  <-- AFTER QApplication
    |
    v
create_html_view()
    |
    +---> ProfileManager.ensure_configured()
    +---> HtmlView.__init__()
    |         |
    |         +--> apply_base_optimizations()
    |
    v
view.set_html(html, custom_css)
    |
    +---> WebViewRegistry.register()
    |         |
    |         +--> Store weak ref + content
    |         +--> Register theme callback (once)
    |
    +---> build_themed_html()
    |         |
    |         +--> generate_theme_stylesheet()
    |         +--> Inject CSS into HTML
    |
    +---> QWebEngineView.setHtml()
              |
              v
          Rendered content

Theme Change Event
    |
    v
StylesheetManager.on_theme_change()
    |
    v
WebViewRegistry._on_theme_change()
    |
    +---> set_theme()
    +---> refresh_all() -> Re-render all views

Performance Notes

  • First WebEngine view takes ~600-800ms to initialize (Chromium warm-up)
  • Subsequent views initialize in ~20-50ms
  • Use freeze()/unfreeze() for views that are temporarily hidden
  • Base RAM usage: ~80-120MB for first view, ~20-40MB per additional view

API Reference

src.shared_services.rendering.documents.api

WebEngine public API for themed HTML display.

This module provides the public interface for creating optimized QWebEngineView widgets with automatic theme support.

IMPORTANT: Before using any WebEngine functionality, call configure_chromium_flags() at the very start of your application, BEFORE creating QApplication or importing other Qt modules.

USAGE::

# At the very start of main.py (before QApplication)
from src.shared_services.rendering.documents.web_engine.api import (
    configure_chromium_flags,
)
configure_chromium_flags()

# Later, after QApplication is created
from src.shared_services.rendering.documents.web_engine.api import (
    configure_default_profile,
    create_html_view,
)

# Configure profile once
configure_default_profile()

# Create views as needed
view = create_html_view(parent=self)
view.set_html("<h1>Willkommen</h1>")

HtmlView

Bases: QWebEngineView

Optimized WebEngine view for displaying themed HTML content.

This widget provides: - Automatic theme integration with the application style system - Optimized settings for local HTML display - Lifecycle management for resource efficiency - Simple API for setting HTML content

The view automatically updates when the application theme changes, re-rendering the content with appropriate colors.

Example::

view = HtmlView(parent=self)
view.set_html("<h1>Willkommen</h1><p>Dies ist ein Test.</p>")

# Or with custom CSS
view.set_html(
    "<div class='card'>Inhalt</div>",
    custom_css=".card { padding: 20px; border-radius: 8px; }"
)
Source code in src\shared_services\rendering\documents\html_view.py
class HtmlView(QWebEngineView):
    """
    Optimized WebEngine view for displaying themed HTML content.

    This widget provides:
    - Automatic theme integration with the application style system
    - Optimized settings for local HTML display
    - Lifecycle management for resource efficiency
    - Simple API for setting HTML content

    The view automatically updates when the application theme changes,
    re-rendering the content with appropriate colors.

    Example::

        view = HtmlView(parent=self)
        view.set_html("<h1>Willkommen</h1><p>Dies ist ein Test.</p>")

        # Or with custom CSS
        view.set_html(
            "<div class='card'>Inhalt</div>",
            custom_css=".card { padding: 20px; border-radius: 8px; }"
        )
    """

    content_loaded = Signal(bool)

    def __init__(
        self,
        parent: Optional[QWidget] = None,
        object_name: str = "HtmlView",
        include_base_styles: bool = True,
    ) -> None:
        """
        Initialize the HTML view.

        Args:
            parent: Parent widget.
            object_name: Object name for styling and identification.
            include_base_styles: Whether to include base theme styles.
        """
        # Ensure profile is configured
        ProfileManager.ensure_configured()

        super().__init__(parent)
        self.setObjectName(object_name)

        self._include_base_styles = include_base_styles
        self._custom_css: Optional[str] = None
        self._html_content: str = ""
        self._is_registered = False

        # Reduce flicker during resize by setting widget attributes
        self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent, True)

        # Apply optimizations
        self._configure_settings()

        # Set initial page background color to match theme (reduces resize flicker)
        self._sync_background_color()

        # Connect load signal
        self.loadFinished.connect(self._on_load_finished)

    def _configure_settings(self) -> None:
        """Configure WebEngine settings for optimal performance."""
        apply_base_optimizations(self)

        # Keep JavaScript enabled for interactivity
        self.settings().setAttribute(
            QWebEngineSettings.WebAttribute.JavascriptEnabled,
            True,
        )

    def _sync_background_color(self) -> None:
        """
        Sync the page background color with the current theme.

        This reduces flicker during resize by ensuring the underlying
        page color matches the content background.
        """
        page = self.page()
        if page:
            bg_color_hex = get_color("background")
            page.setBackgroundColor(QColor(bg_color_hex))

    def set_html(
        self,
        html: str,
        custom_css: Optional[str] = None,
        register_for_theme: bool = True,
    ) -> None:
        """
        Set the HTML content to display.

        The content is automatically wrapped with theme-appropriate
        styles and will update when the application theme changes.

        Args:
            html: HTML content (can be fragment or complete document).
            custom_css: Optional custom CSS to include.
            register_for_theme: Whether to register for automatic theme updates.

        Example::

            view.set_html("<h1>Titel</h1><p>Absatz mit Text.</p>")

            # With custom styles
            view.set_html(
                "<table><tr><td>Zelle</td></tr></table>",
                custom_css="table { margin: 20px; }"
            )
        """
        self._html_content = html
        self._custom_css = custom_css

        # Sync background color before rendering to reduce flicker
        self._sync_background_color()

        if register_for_theme:
            registry = get_web_view_registry()
            if self._is_registered:
                registry.update_content(
                    self, html, self._include_base_styles, custom_css
                )
            else:
                registry.register(
                    self, html, self._include_base_styles, custom_css
                )
                self._is_registered = True
        else:
            # Just render without registration
            final_html = build_themed_html(
                html,
                include_base_styles=self._include_base_styles,
                custom_css=custom_css,
            )
            self.setHtml(final_html)

    def setHtml(self, html: str, baseUrl: Optional[str] = None) -> None:
        """
        Override setHtml to sync background color before rendering.

        This ensures the page background matches the theme, reducing
        flicker during resize operations.
        """
        self._sync_background_color()
        if baseUrl:
            super().setHtml(html, baseUrl)
        else:
            super().setHtml(html)

    def set_html_raw(self, html: str) -> None:
        """
        Set raw HTML without theme styles or registration.

        Use this for content that manages its own styling.

        Args:
            html: Complete HTML document.
        """
        self._unregister()
        self._html_content = html
        self.setHtml(html)

    def get_html_content(self) -> str:
        """
        Get the current HTML content (without theme wrapper).

        Returns:
            The HTML content that was set.
        """
        return self._html_content

    def refresh(self) -> None:
        """
        Refresh the view with current content and theme.

        Call this after modifying content dynamically.
        """
        # Sync background color in case theme changed
        self._sync_background_color()

        if self._is_registered:
            registry = get_web_view_registry()
            registry.refresh_view(self)
        elif self._html_content:
            final_html = build_themed_html(
                self._html_content,
                include_base_styles=self._include_base_styles,
                custom_css=self._custom_css,
            )
            self.setHtml(final_html)

    def set_custom_css(self, css: str) -> None:
        """
        Set or update custom CSS.

        Args:
            css: Custom CSS rules.
        """
        self._custom_css = css
        self.refresh()

    def freeze(self) -> None:
        """
        Freeze the view to reduce CPU usage when hidden.

        Call this when the view is not visible to save resources.
        The view will still display its last content but won't
        process JavaScript or animations.
        """
        page = self.page()
        if page:
            page.setLifecycleState(QWebEnginePage.LifecycleState.Frozen)

    def unfreeze(self) -> None:
        """
        Unfreeze the view when it becomes visible again.

        Restores normal operation after a freeze() call.
        """
        page = self.page()
        if page:
            page.setLifecycleState(QWebEnginePage.LifecycleState.Active)

    def _on_load_finished(self, success: bool) -> None:
        """Handle load completion."""
        self.content_loaded.emit(success)

    def _unregister(self) -> None:
        """Unregister from theme updates."""
        if self._is_registered:
            registry = get_web_view_registry()
            registry.unregister(self)
            self._is_registered = False

    def hideEvent(self, event) -> None:
        """Handle hide event - optionally freeze."""
        super().hideEvent(event)
        # Note: Auto-freeze is not enabled by default
        # Uncomment below if you want automatic freeze on hide
        # self.freeze()

    def showEvent(self, event) -> None:
        """Handle show event - unfreeze if needed."""
        super().showEvent(event)
        page = self.page()
        if page and page.lifecycleState() == QWebEnginePage.LifecycleState.Frozen:
            self.unfreeze()

    def closeEvent(self, event) -> None:
        """Clean up on close."""
        self._unregister()
        super().closeEvent(event)
__init__(parent=None, object_name='HtmlView', include_base_styles=True)

Initialize the HTML view.

Parameters:

Name Type Description Default
parent Optional[QWidget]

Parent widget.

None
object_name str

Object name for styling and identification.

'HtmlView'
include_base_styles bool

Whether to include base theme styles.

True
Source code in src\shared_services\rendering\documents\html_view.py
def __init__(
    self,
    parent: Optional[QWidget] = None,
    object_name: str = "HtmlView",
    include_base_styles: bool = True,
) -> None:
    """
    Initialize the HTML view.

    Args:
        parent: Parent widget.
        object_name: Object name for styling and identification.
        include_base_styles: Whether to include base theme styles.
    """
    # Ensure profile is configured
    ProfileManager.ensure_configured()

    super().__init__(parent)
    self.setObjectName(object_name)

    self._include_base_styles = include_base_styles
    self._custom_css: Optional[str] = None
    self._html_content: str = ""
    self._is_registered = False

    # Reduce flicker during resize by setting widget attributes
    self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent, True)

    # Apply optimizations
    self._configure_settings()

    # Set initial page background color to match theme (reduces resize flicker)
    self._sync_background_color()

    # Connect load signal
    self.loadFinished.connect(self._on_load_finished)
closeEvent(event)

Clean up on close.

Source code in src\shared_services\rendering\documents\html_view.py
def closeEvent(self, event) -> None:
    """Clean up on close."""
    self._unregister()
    super().closeEvent(event)
freeze()

Freeze the view to reduce CPU usage when hidden.

Call this when the view is not visible to save resources. The view will still display its last content but won't process JavaScript or animations.

Source code in src\shared_services\rendering\documents\html_view.py
def freeze(self) -> None:
    """
    Freeze the view to reduce CPU usage when hidden.

    Call this when the view is not visible to save resources.
    The view will still display its last content but won't
    process JavaScript or animations.
    """
    page = self.page()
    if page:
        page.setLifecycleState(QWebEnginePage.LifecycleState.Frozen)
get_html_content()

Get the current HTML content (without theme wrapper).

Returns:

Type Description
str

The HTML content that was set.

Source code in src\shared_services\rendering\documents\html_view.py
def get_html_content(self) -> str:
    """
    Get the current HTML content (without theme wrapper).

    Returns:
        The HTML content that was set.
    """
    return self._html_content
hideEvent(event)

Handle hide event - optionally freeze.

Source code in src\shared_services\rendering\documents\html_view.py
def hideEvent(self, event) -> None:
    """Handle hide event - optionally freeze."""
    super().hideEvent(event)
refresh()

Refresh the view with current content and theme.

Call this after modifying content dynamically.

Source code in src\shared_services\rendering\documents\html_view.py
def refresh(self) -> None:
    """
    Refresh the view with current content and theme.

    Call this after modifying content dynamically.
    """
    # Sync background color in case theme changed
    self._sync_background_color()

    if self._is_registered:
        registry = get_web_view_registry()
        registry.refresh_view(self)
    elif self._html_content:
        final_html = build_themed_html(
            self._html_content,
            include_base_styles=self._include_base_styles,
            custom_css=self._custom_css,
        )
        self.setHtml(final_html)
setHtml(html, baseUrl=None)

Override setHtml to sync background color before rendering.

This ensures the page background matches the theme, reducing flicker during resize operations.

Source code in src\shared_services\rendering\documents\html_view.py
def setHtml(self, html: str, baseUrl: Optional[str] = None) -> None:
    """
    Override setHtml to sync background color before rendering.

    This ensures the page background matches the theme, reducing
    flicker during resize operations.
    """
    self._sync_background_color()
    if baseUrl:
        super().setHtml(html, baseUrl)
    else:
        super().setHtml(html)
set_custom_css(css)

Set or update custom CSS.

Parameters:

Name Type Description Default
css str

Custom CSS rules.

required
Source code in src\shared_services\rendering\documents\html_view.py
def set_custom_css(self, css: str) -> None:
    """
    Set or update custom CSS.

    Args:
        css: Custom CSS rules.
    """
    self._custom_css = css
    self.refresh()
set_html(html, custom_css=None, register_for_theme=True)

Set the HTML content to display.

The content is automatically wrapped with theme-appropriate styles and will update when the application theme changes.

Parameters:

Name Type Description Default
html str

HTML content (can be fragment or complete document).

required
custom_css Optional[str]

Optional custom CSS to include.

None
register_for_theme bool

Whether to register for automatic theme updates.

True

Example::

view.set_html("<h1>Titel</h1><p>Absatz mit Text.</p>")

# With custom styles
view.set_html(
    "<table><tr><td>Zelle</td></tr></table>",
    custom_css="table { margin: 20px; }"
)
Source code in src\shared_services\rendering\documents\html_view.py
def set_html(
    self,
    html: str,
    custom_css: Optional[str] = None,
    register_for_theme: bool = True,
) -> None:
    """
    Set the HTML content to display.

    The content is automatically wrapped with theme-appropriate
    styles and will update when the application theme changes.

    Args:
        html: HTML content (can be fragment or complete document).
        custom_css: Optional custom CSS to include.
        register_for_theme: Whether to register for automatic theme updates.

    Example::

        view.set_html("<h1>Titel</h1><p>Absatz mit Text.</p>")

        # With custom styles
        view.set_html(
            "<table><tr><td>Zelle</td></tr></table>",
            custom_css="table { margin: 20px; }"
        )
    """
    self._html_content = html
    self._custom_css = custom_css

    # Sync background color before rendering to reduce flicker
    self._sync_background_color()

    if register_for_theme:
        registry = get_web_view_registry()
        if self._is_registered:
            registry.update_content(
                self, html, self._include_base_styles, custom_css
            )
        else:
            registry.register(
                self, html, self._include_base_styles, custom_css
            )
            self._is_registered = True
    else:
        # Just render without registration
        final_html = build_themed_html(
            html,
            include_base_styles=self._include_base_styles,
            custom_css=custom_css,
        )
        self.setHtml(final_html)
set_html_raw(html)

Set raw HTML without theme styles or registration.

Use this for content that manages its own styling.

Parameters:

Name Type Description Default
html str

Complete HTML document.

required
Source code in src\shared_services\rendering\documents\html_view.py
def set_html_raw(self, html: str) -> None:
    """
    Set raw HTML without theme styles or registration.

    Use this for content that manages its own styling.

    Args:
        html: Complete HTML document.
    """
    self._unregister()
    self._html_content = html
    self.setHtml(html)
showEvent(event)

Handle show event - unfreeze if needed.

Source code in src\shared_services\rendering\documents\html_view.py
def showEvent(self, event) -> None:
    """Handle show event - unfreeze if needed."""
    super().showEvent(event)
    page = self.page()
    if page and page.lifecycleState() == QWebEnginePage.LifecycleState.Frozen:
        self.unfreeze()
unfreeze()

Unfreeze the view when it becomes visible again.

Restores normal operation after a freeze() call.

Source code in src\shared_services\rendering\documents\html_view.py
def unfreeze(self) -> None:
    """
    Unfreeze the view when it becomes visible again.

    Restores normal operation after a freeze() call.
    """
    page = self.page()
    if page:
        page.setLifecycleState(QWebEnginePage.LifecycleState.Active)

HtmlViewMinimal

Bases: QWebEngineView

Minimal WebEngine view without theme integration.

Use this for cases where you want full control over styling or when displaying external content that should not be themed.

This view applies performance optimizations but does not inject theme styles or register for theme updates.

Source code in src\shared_services\rendering\documents\html_view.py
class HtmlViewMinimal(QWebEngineView):
    """
    Minimal WebEngine view without theme integration.

    Use this for cases where you want full control over styling
    or when displaying external content that should not be themed.

    This view applies performance optimizations but does not
    inject theme styles or register for theme updates.
    """

    def __init__(
        self,
        parent: Optional[QWidget] = None,
        object_name: str = "HtmlViewMinimal",
    ) -> None:
        """
        Initialize the minimal HTML view.

        Args:
            parent: Parent widget.
            object_name: Object name for identification.
        """
        ProfileManager.ensure_configured()

        super().__init__(parent)
        self.setObjectName(object_name)

        # Reduce flicker during resize
        self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent, True)

        apply_base_optimizations(self)

    def set_background_color(self, color: str) -> None:
        """
        Set the page background color.

        Call this to reduce flicker during resize by matching the
        Chromium page background to your content color.

        Args:
            color: Hex color string (e.g., "#ffffff").
        """
        page = self.page()
        if page:
            page.setBackgroundColor(QColor(color))
__init__(parent=None, object_name='HtmlViewMinimal')

Initialize the minimal HTML view.

Parameters:

Name Type Description Default
parent Optional[QWidget]

Parent widget.

None
object_name str

Object name for identification.

'HtmlViewMinimal'
Source code in src\shared_services\rendering\documents\html_view.py
def __init__(
    self,
    parent: Optional[QWidget] = None,
    object_name: str = "HtmlViewMinimal",
) -> None:
    """
    Initialize the minimal HTML view.

    Args:
        parent: Parent widget.
        object_name: Object name for identification.
    """
    ProfileManager.ensure_configured()

    super().__init__(parent)
    self.setObjectName(object_name)

    # Reduce flicker during resize
    self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent, True)

    apply_base_optimizations(self)
set_background_color(color)

Set the page background color.

Call this to reduce flicker during resize by matching the Chromium page background to your content color.

Parameters:

Name Type Description Default
color str

Hex color string (e.g., "#ffffff").

required
Source code in src\shared_services\rendering\documents\html_view.py
def set_background_color(self, color: str) -> None:
    """
    Set the page background color.

    Call this to reduce flicker during resize by matching the
    Chromium page background to your content color.

    Args:
        color: Hex color string (e.g., "#ffffff").
    """
    page = self.page()
    if page:
        page.setBackgroundColor(QColor(color))

ProfileManager

Singleton manager for WebEngine profile configuration.

Ensures profile is configured exactly once and provides access to the configured profile.

Source code in src\shared_services\rendering\documents\profile_config.py
class ProfileManager:
    """
    Singleton manager for WebEngine profile configuration.

    Ensures profile is configured exactly once and provides
    access to the configured profile.
    """

    _instance: Optional["ProfileManager"] = None
    _profile_configured: bool = False

    def __new__(cls) -> "ProfileManager":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    @classmethod
    def instance(cls) -> "ProfileManager":
        """Get the singleton instance."""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    @classmethod
    def ensure_configured(cls) -> None:
        """
        Ensure the default profile is configured.

        Safe to call multiple times - configuration only happens once.
        """
        if cls._profile_configured:
            return

        configure_default_profile()
        cls._profile_configured = True

    @classmethod
    def get_profile(cls) -> QWebEngineProfile:
        """
        Get the configured default profile.

        Ensures configuration before returning.

        Returns:
            The configured QWebEngineProfile.
        """
        cls.ensure_configured()
        return QWebEngineProfile.defaultProfile()
ensure_configured() classmethod

Ensure the default profile is configured.

Safe to call multiple times - configuration only happens once.

Source code in src\shared_services\rendering\documents\profile_config.py
@classmethod
def ensure_configured(cls) -> None:
    """
    Ensure the default profile is configured.

    Safe to call multiple times - configuration only happens once.
    """
    if cls._profile_configured:
        return

    configure_default_profile()
    cls._profile_configured = True
get_profile() classmethod

Get the configured default profile.

Ensures configuration before returning.

Returns:

Type Description
QWebEngineProfile

The configured QWebEngineProfile.

Source code in src\shared_services\rendering\documents\profile_config.py
@classmethod
def get_profile(cls) -> QWebEngineProfile:
    """
    Get the configured default profile.

    Ensures configuration before returning.

    Returns:
        The configured QWebEngineProfile.
    """
    cls.ensure_configured()
    return QWebEngineProfile.defaultProfile()
instance() classmethod

Get the singleton instance.

Source code in src\shared_services\rendering\documents\profile_config.py
@classmethod
def instance(cls) -> "ProfileManager":
    """Get the singleton instance."""
    if cls._instance is None:
        cls._instance = cls()
    return cls._instance

WebViewRegistry

Registry for tracking WebEngine views for theme updates.

Uses weak references to avoid memory leaks. When a view widget is garbage collected, it is automatically removed from the registry.

The registry registers a callback with StylesheetManager to be notified when the theme changes, triggering re-render of all registered views.

Source code in src\shared_services\rendering\documents\web_view_registry.py
class WebViewRegistry:
    """
    Registry for tracking WebEngine views for theme updates.

    Uses weak references to avoid memory leaks. When a view widget
    is garbage collected, it is automatically removed from the registry.

    The registry registers a callback with StylesheetManager to be
    notified when the theme changes, triggering re-render of all
    registered views.
    """

    _instance: Optional["WebViewRegistry"] = None

    def __new__(cls) -> "WebViewRegistry":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self) -> None:
        if self._initialized:
            return
        self._initialized = True

        self._views: Dict[int, weakref.ref] = {}
        self._view_data: Dict[int, RegisteredWebView] = {}
        self._theme_callback_registered = False

    @classmethod
    def instance(cls) -> "WebViewRegistry":
        """Get the singleton instance."""
        if cls._instance is None:
            cls._instance = cls()
        return cls._instance

    def register(
        self,
        view: QWebEngineView,
        html_content: str,
        include_base_styles: bool = True,
        custom_css: Optional[str] = None,
    ) -> None:
        """
        Register a WebEngine view for automatic theme updates.

        Args:
            view: The QWebEngineView widget to track.
            html_content: The HTML source content.
            include_base_styles: Whether to include base theme styles.
            custom_css: Optional custom CSS to include.
        """
        self._ensure_theme_callback()

        view_id = id(view)

        def cleanup(ref: weakref.ref) -> None:
            self._views.pop(view_id, None)
            self._view_data.pop(view_id, None)

        self._views[view_id] = weakref.ref(view, cleanup)
        self._view_data[view_id] = RegisteredWebView(
            html_content=html_content,
            include_base_styles=include_base_styles,
            custom_css=custom_css,
        )

        # Render immediately
        self._render_view(view, html_content, include_base_styles, custom_css)

    def unregister(self, view: QWebEngineView) -> None:
        """
        Unregister a view from theme updates.

        Args:
            view: The QWebEngineView widget to stop tracking.
        """
        view_id = id(view)
        self._views.pop(view_id, None)
        self._view_data.pop(view_id, None)

    def update_content(
        self,
        view: QWebEngineView,
        html_content: str,
        include_base_styles: Optional[bool] = None,
        custom_css: Optional[str] = None,
    ) -> None:
        """
        Update the content of a registered view.

        Args:
            view: The registered QWebEngineView.
            html_content: New HTML source content.
            include_base_styles: Update style inclusion (or keep existing).
            custom_css: Update custom CSS (or keep existing).
        """
        view_id = id(view)
        if view_id in self._view_data:
            data = self._view_data[view_id]
            data.html_content = html_content
            if include_base_styles is not None:
                data.include_base_styles = include_base_styles
            if custom_css is not None:
                data.custom_css = custom_css
            self._render_view(
                view, html_content, data.include_base_styles, data.custom_css
            )
        else:
            # Not registered, just render
            self._render_view(
                view,
                html_content,
                include_base_styles if include_base_styles is not None else True,
                custom_css,
            )

    def refresh_all(self) -> None:
        """
        Refresh all registered views with current theme.

        Called automatically when theme changes.
        """
        dead_ids: Set[int] = set()

        for view_id, ref in self._views.items():
            view = ref()
            if view is None:
                dead_ids.add(view_id)
            else:
                data = self._view_data.get(view_id)
                if data:
                    self._render_view(
                        view,
                        data.html_content,
                        data.include_base_styles,
                        data.custom_css,
                    )

        # Remove dead references
        for view_id in dead_ids:
            self._views.pop(view_id, None)
            self._view_data.pop(view_id, None)

    def refresh_view(self, view: QWebEngineView) -> None:
        """
        Refresh a specific view with current theme.

        Args:
            view: The QWebEngineView to refresh.
        """
        view_id = id(view)
        data = self._view_data.get(view_id)
        if data:
            self._render_view(
                view, data.html_content, data.include_base_styles, data.custom_css
            )

    def _render_view(
        self,
        view: QWebEngineView,
        html_content: str,
        include_base_styles: bool,
        custom_css: Optional[str],
    ) -> None:
        """Render HTML with theme styles to the view."""
        final_html = build_themed_html(
            html_content,
            include_base_styles=include_base_styles,
            custom_css=custom_css,
        )
        view.setHtml(final_html)

    def _ensure_theme_callback(self) -> None:
        """Ensure the theme change callback is registered."""
        if self._theme_callback_registered:
            return

        from src.shared_services.logging.logger_factory import get_logger

        logger = get_logger()

        try:
            from src.shared_services.rendering.stylesheets.stylesheet_manager import (
                get_stylesheet_manager,
            )

            manager = get_stylesheet_manager()
            manager.on_theme_change(self._on_theme_change)
            self._theme_callback_registered = True
            logger.debug("WebViewRegistry: Theme callback registered")
        except ImportError as e:
            logger.warning(f"WebViewRegistry: Failed to register theme callback - {e}")
        except Exception as e:
            logger.error(f"WebViewRegistry: Error registering theme callback - {e}")

    def _on_theme_change(self, theme: str) -> None:
        """Handle theme change notification."""
        from src.shared_services.logging.logger_factory import get_logger

        logger = get_logger()
        logger.debug(
            f"WebViewRegistry: Theme change to '{theme}', "
            f"updating {len(self._views)} views"
        )

        # Update web_view_colors theme
        web_view_colors.set_theme(theme)

        # Re-render all views
        self.refresh_all()
instance() classmethod

Get the singleton instance.

Source code in src\shared_services\rendering\documents\web_view_registry.py
@classmethod
def instance(cls) -> "WebViewRegistry":
    """Get the singleton instance."""
    if cls._instance is None:
        cls._instance = cls()
    return cls._instance
refresh_all()

Refresh all registered views with current theme.

Called automatically when theme changes.

Source code in src\shared_services\rendering\documents\web_view_registry.py
def refresh_all(self) -> None:
    """
    Refresh all registered views with current theme.

    Called automatically when theme changes.
    """
    dead_ids: Set[int] = set()

    for view_id, ref in self._views.items():
        view = ref()
        if view is None:
            dead_ids.add(view_id)
        else:
            data = self._view_data.get(view_id)
            if data:
                self._render_view(
                    view,
                    data.html_content,
                    data.include_base_styles,
                    data.custom_css,
                )

    # Remove dead references
    for view_id in dead_ids:
        self._views.pop(view_id, None)
        self._view_data.pop(view_id, None)
refresh_view(view)

Refresh a specific view with current theme.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView to refresh.

required
Source code in src\shared_services\rendering\documents\web_view_registry.py
def refresh_view(self, view: QWebEngineView) -> None:
    """
    Refresh a specific view with current theme.

    Args:
        view: The QWebEngineView to refresh.
    """
    view_id = id(view)
    data = self._view_data.get(view_id)
    if data:
        self._render_view(
            view, data.html_content, data.include_base_styles, data.custom_css
        )
register(view, html_content, include_base_styles=True, custom_css=None)

Register a WebEngine view for automatic theme updates.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView widget to track.

required
html_content str

The HTML source content.

required
include_base_styles bool

Whether to include base theme styles.

True
custom_css Optional[str]

Optional custom CSS to include.

None
Source code in src\shared_services\rendering\documents\web_view_registry.py
def register(
    self,
    view: QWebEngineView,
    html_content: str,
    include_base_styles: bool = True,
    custom_css: Optional[str] = None,
) -> None:
    """
    Register a WebEngine view for automatic theme updates.

    Args:
        view: The QWebEngineView widget to track.
        html_content: The HTML source content.
        include_base_styles: Whether to include base theme styles.
        custom_css: Optional custom CSS to include.
    """
    self._ensure_theme_callback()

    view_id = id(view)

    def cleanup(ref: weakref.ref) -> None:
        self._views.pop(view_id, None)
        self._view_data.pop(view_id, None)

    self._views[view_id] = weakref.ref(view, cleanup)
    self._view_data[view_id] = RegisteredWebView(
        html_content=html_content,
        include_base_styles=include_base_styles,
        custom_css=custom_css,
    )

    # Render immediately
    self._render_view(view, html_content, include_base_styles, custom_css)
unregister(view)

Unregister a view from theme updates.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView widget to stop tracking.

required
Source code in src\shared_services\rendering\documents\web_view_registry.py
def unregister(self, view: QWebEngineView) -> None:
    """
    Unregister a view from theme updates.

    Args:
        view: The QWebEngineView widget to stop tracking.
    """
    view_id = id(view)
    self._views.pop(view_id, None)
    self._view_data.pop(view_id, None)
update_content(view, html_content, include_base_styles=None, custom_css=None)

Update the content of a registered view.

Parameters:

Name Type Description Default
view QWebEngineView

The registered QWebEngineView.

required
html_content str

New HTML source content.

required
include_base_styles Optional[bool]

Update style inclusion (or keep existing).

None
custom_css Optional[str]

Update custom CSS (or keep existing).

None
Source code in src\shared_services\rendering\documents\web_view_registry.py
def update_content(
    self,
    view: QWebEngineView,
    html_content: str,
    include_base_styles: Optional[bool] = None,
    custom_css: Optional[str] = None,
) -> None:
    """
    Update the content of a registered view.

    Args:
        view: The registered QWebEngineView.
        html_content: New HTML source content.
        include_base_styles: Update style inclusion (or keep existing).
        custom_css: Update custom CSS (or keep existing).
    """
    view_id = id(view)
    if view_id in self._view_data:
        data = self._view_data[view_id]
        data.html_content = html_content
        if include_base_styles is not None:
            data.include_base_styles = include_base_styles
        if custom_css is not None:
            data.custom_css = custom_css
        self._render_view(
            view, html_content, data.include_base_styles, data.custom_css
        )
    else:
        # Not registered, just render
        self._render_view(
            view,
            html_content,
            include_base_styles if include_base_styles is not None else True,
            custom_css,
        )

apply_base_optimizations(view)

Apply base optimizations to a WebEngineView.

These settings disable browser features not needed for local HTML display without any functionality loss for typical desktop app use cases.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView to configure.

required
Source code in src\shared_services\rendering\documents\view_settings.py
def apply_base_optimizations(view: QWebEngineView) -> None:
    """
    Apply base optimizations to a WebEngineView.

    These settings disable browser features not needed for
    local HTML display without any functionality loss for
    typical desktop app use cases.

    Args:
        view: The QWebEngineView to configure.
    """
    settings = view.settings()

    # Browser plugins (Flash etc.) - obsolete
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.PluginsEnabled,
        False,
    )

    # PDF viewer - not needed for HTML display
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.PdfViewerEnabled,
        False,
    )

    # Screen capture API - not needed
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.ScreenCaptureEnabled,
        False,
    )

    # Favicon loading - not needed for local content
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.AutoLoadIconsForPage,
        False,
    )
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.TouchIconsEnabled,
        False,
    )

    # Media autoplay - require user gesture
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.PlaybackRequiresUserGesture,
        True,
    )

    # WebRTC - restrict to public interfaces only
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.WebRTCPublicInterfacesOnly,
        True,
    )

    # WebGL - disabled for non-3D content (saves 20-50 MB)
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.WebGLEnabled,
        False,
    )

    # Local storage - not needed for stateless display
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.LocalStorageEnabled,
        False,
    )

    # JavaScript popup windows - prevent
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.JavascriptCanOpenWindows,
        False,
    )

    # Clipboard access - not needed
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.JavascriptCanAccessClipboard,
        False,
    )

    # Enable smooth scrolling animation
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.ScrollAnimatorEnabled,
        True,
    )

apply_full_optimizations(view)

Apply full optimizations including features that might affect some content.

Use this for views that display simple, known content where these features are confirmed unnecessary.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView to configure.

required
Source code in src\shared_services\rendering\documents\view_settings.py
def apply_full_optimizations(view: QWebEngineView) -> None:
    """
    Apply full optimizations including features that might
    affect some content.

    Use this for views that display simple, known content
    where these features are confirmed unnecessary.

    Args:
        view: The QWebEngineView to configure.
    """
    # First apply base optimizations
    apply_base_optimizations(view)

    settings = view.settings()

    # Accelerated 2D canvas - disable if not using canvas
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.Accelerated2dCanvasEnabled,
        False,
    )

    # DNS prefetch - not needed for local content
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.DnsPrefetchEnabled,
        False,
    )

    # Error pages - use custom handling instead
    settings.setAttribute(
        QWebEngineSettings.WebAttribute.ErrorPageEnabled,
        False,
    )

build_themed_html(content, include_base_styles=True, custom_css=None, theme=None)

Build complete HTML document with theme styles.

If the content already contains or tags, styles are injected into the existing structure. Otherwise, a complete HTML document wrapper is created.

Scrollbar styles are always included regardless of include_base_styles to ensure consistent scrollbar appearance across all web views.

Parameters:

Name Type Description Default
content str

HTML content (can be fragment or complete document).

required
include_base_styles bool

Whether to include base theme styles.

True
custom_css Optional[str]

Optional custom CSS to include.

None
theme Optional[str]

Theme to use (default: current theme).

None

Returns:

Type Description
str

Complete HTML document string with theme styles.

Source code in src\shared_services\rendering\documents\web_view_registry.py
def build_themed_html(
    content: str,
    include_base_styles: bool = True,
    custom_css: Optional[str] = None,
    theme: Optional[str] = None,
) -> str:
    """
    Build complete HTML document with theme styles.

    If the content already contains <html> or <body> tags, styles
    are injected into the existing structure. Otherwise, a complete
    HTML document wrapper is created.

    Scrollbar styles are always included regardless of include_base_styles
    to ensure consistent scrollbar appearance across all web views.

    Args:
        content: HTML content (can be fragment or complete document).
        include_base_styles: Whether to include base theme styles.
        custom_css: Optional custom CSS to include.
        theme: Theme to use (default: current theme).

    Returns:
        Complete HTML document string with theme styles.
    """
    # Generate theme CSS
    style_parts = []

    # Always include scrollbar styles for consistent appearance
    style_parts.append(web_view_colors.generate_scrollbar_styles(theme))

    if include_base_styles:
        style_parts.append(web_view_colors.generate_theme_stylesheet(theme))

    if custom_css:
        style_parts.append(custom_css)

    style_block = "\n".join(style_parts)

    # Check if content is already a complete document
    content_lower = content.lower()
    has_html_tag = "<html" in content_lower
    has_head_tag = "<head" in content_lower

    if has_html_tag and has_head_tag:
        # Inject style into existing head
        # Find the end of the opening head tag
        head_end = content_lower.find(">", content_lower.find("<head"))
        if head_end != -1:
            insert_pos = head_end + 1
            return (
                content[:insert_pos]
                + f"\n<style>\n{style_block}\n</style>\n"
                + content[insert_pos:]
            )

    if has_html_tag:
        # Has html but no head - insert head with style
        html_end = content_lower.find(">", content_lower.find("<html"))
        if html_end != -1:
            insert_pos = html_end + 1
            return (
                content[:insert_pos]
                + f"\n<head>\n<meta charset=\"utf-8\">\n<style>\n{style_block}\n</style>\n</head>\n"
                + content[insert_pos:]
            )

    # No html tag - wrap in complete document
    return f"""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
{style_block}
</style>
</head>
<body>
{content}
</body>
</html>"""

configure_chromium_flags()

Configure Chromium flags for optimized resource usage.

IMPORTANT: This must be called BEFORE creating QApplication or any Qt imports that trigger Qt initialization.

The flags disable unnecessary browser features to reduce memory footprint and background activity.

Example::

# At the very start of main.py, before other imports
from src.shared_services.rendering.documents.web_engine.profile_config import (
    configure_chromium_flags,
)
configure_chromium_flags()

# Then proceed with normal imports
from PySide6.QtWidgets import QApplication
Source code in src\shared_services\rendering\documents\profile_config.py
def configure_chromium_flags() -> None:
    """
    Configure Chromium flags for optimized resource usage.

    IMPORTANT: This must be called BEFORE creating QApplication
    or any Qt imports that trigger Qt initialization.

    The flags disable unnecessary browser features to reduce
    memory footprint and background activity.

    Example::

        # At the very start of main.py, before other imports
        from src.shared_services.rendering.documents.web_engine.profile_config import (
            configure_chromium_flags,
        )
        configure_chromium_flags()

        # Then proceed with normal imports
        from PySide6.QtWidgets import QApplication
    """
    existing = os.environ.get("QTWEBENGINE_CHROMIUM_FLAGS", "")
    if existing:
        # Append to existing flags
        flags = existing + " " + " ".join(CHROMIUM_FLAGS)
    else:
        flags = " ".join(CHROMIUM_FLAGS)

    os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = flags

configure_default_profile()

Configure the default WebEngine profile for desktop app usage.

Disables persistence features not needed in a local desktop application context (cookies, spellcheck, cache).

Call this once after QApplication is created, before creating any QWebEngineView instances.

Example::

app = QApplication(sys.argv)
configure_default_profile()
Source code in src\shared_services\rendering\documents\profile_config.py
def configure_default_profile() -> None:
    """
    Configure the default WebEngine profile for desktop app usage.

    Disables persistence features not needed in a local desktop
    application context (cookies, spellcheck, cache).

    Call this once after QApplication is created, before creating
    any QWebEngineView instances.

    Example::

        app = QApplication(sys.argv)
        configure_default_profile()
    """
    from src.shared_services.logging.logger_factory import get_logger

    logger = get_logger()

    profile = QWebEngineProfile.defaultProfile()

    # Disable spellcheck - not relevant for display-only content
    profile.setSpellCheckEnabled(False)

    # No persistent cookies - local app does not need them
    profile.setPersistentCookiesPolicy(
        QWebEngineProfile.PersistentCookiesPolicy.NoPersistentCookies
    )

    # No HTTP cache - content is generated locally
    profile.setHttpCacheType(QWebEngineProfile.HttpCacheType.NoCache)

    logger.debug("WebEngine: Default profile configured for desktop app usage")

create_html_view(parent=None, object_name='HtmlView', include_base_styles=True)

Create a theme-aware HTML view widget.

The returned view automatically integrates with the application theme system and updates when the theme changes.

Parameters:

Name Type Description Default
parent Optional[QWidget]

Parent widget.

None
object_name str

Object name for styling and identification.

'HtmlView'
include_base_styles bool

Whether to include base theme styles.

True

Returns:

Type Description
HtmlView

Configured HtmlView instance.

Example::

view = create_html_view(parent=self, object_name="NewsView")
view.set_html("<h1>Neuigkeiten</h1><p>Aktuelle Updates...</p>")
Source code in src\shared_services\rendering\documents\api.py
def create_html_view(
    parent: Optional[QWidget] = None,
    object_name: str = "HtmlView",
    include_base_styles: bool = True,
) -> HtmlView:
    """
    Create a theme-aware HTML view widget.

    The returned view automatically integrates with the application
    theme system and updates when the theme changes.

    Args:
        parent: Parent widget.
        object_name: Object name for styling and identification.
        include_base_styles: Whether to include base theme styles.

    Returns:
        Configured HtmlView instance.

    Example::

        view = create_html_view(parent=self, object_name="NewsView")
        view.set_html("<h1>Neuigkeiten</h1><p>Aktuelle Updates...</p>")
    """
    return HtmlView(
        parent=parent,
        object_name=object_name,
        include_base_styles=include_base_styles,
    )

create_html_view_minimal(parent=None, object_name='HtmlViewMinimal')

Create a minimal HTML view without theme integration.

Use this for content that manages its own styling or external content that should not be themed.

Parameters:

Name Type Description Default
parent Optional[QWidget]

Parent widget.

None
object_name str

Object name for identification.

'HtmlViewMinimal'

Returns:

Type Description
HtmlViewMinimal

Configured HtmlViewMinimal instance.

Source code in src\shared_services\rendering\documents\api.py
def create_html_view_minimal(
    parent: Optional[QWidget] = None,
    object_name: str = "HtmlViewMinimal",
) -> HtmlViewMinimal:
    """
    Create a minimal HTML view without theme integration.

    Use this for content that manages its own styling or
    external content that should not be themed.

    Args:
        parent: Parent widget.
        object_name: Object name for identification.

    Returns:
        Configured HtmlViewMinimal instance.
    """
    return HtmlViewMinimal(parent=parent, object_name=object_name)

enable_javascript(view, enabled=True)

Enable or disable JavaScript execution.

JavaScript is enabled by default. Use this to explicitly control JavaScript for specific views.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView to configure.

required
enabled bool

Whether JavaScript should be enabled.

True
Source code in src\shared_services\rendering\documents\view_settings.py
def enable_javascript(view: QWebEngineView, enabled: bool = True) -> None:
    """
    Enable or disable JavaScript execution.

    JavaScript is enabled by default. Use this to explicitly
    control JavaScript for specific views.

    Args:
        view: The QWebEngineView to configure.
        enabled: Whether JavaScript should be enabled.
    """
    view.settings().setAttribute(
        QWebEngineSettings.WebAttribute.JavascriptEnabled,
        enabled,
    )

enable_scrollbars(view, enabled=True)

Enable or disable scrollbars.

Parameters:

Name Type Description Default
view QWebEngineView

The QWebEngineView to configure.

required
enabled bool

Whether scrollbars should be shown.

True
Source code in src\shared_services\rendering\documents\view_settings.py
def enable_scrollbars(view: QWebEngineView, enabled: bool = True) -> None:
    """
    Enable or disable scrollbars.

    Args:
        view: The QWebEngineView to configure.
        enabled: Whether scrollbars should be shown.
    """
    view.settings().setAttribute(
        QWebEngineSettings.WebAttribute.ShowScrollBars,
        enabled,
    )

generate_base_styles(theme=None)

Generate base CSS styles for HTML content.

Provides sensible defaults for common HTML elements using theme-appropriate colors.

Parameters:

Name Type Description Default
theme Optional[str]

Theme to generate for (default: current theme).

None

Returns:

Type Description
str

CSS string with base element styles.

Source code in src\shared_services\rendering\documents\web_view_colors.py
def generate_base_styles(theme: Optional[str] = None) -> str:
    """
    Generate base CSS styles for HTML content.

    Provides sensible defaults for common HTML elements
    using theme-appropriate colors.

    Args:
        theme: Theme to generate for (default: current theme).

    Returns:
        CSS string with base element styles.
    """
    use_theme = theme if theme else _current_theme
    colors = ColorSystem.get_colors(use_theme)

    styles = f"""
/* Base styles */
* {{
  box-sizing: border-box;
}}

html, body {{
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  color: {colors['text_primary']};
  background-color: {colors['background']};
}}

/* Headings */
h1, h2, h3, h4, h5, h6 {{
  margin-top: 1em;
  margin-bottom: 0.5em;
  font-weight: 600;
  color: {colors['text_primary']};
}}

h1 {{
  font-size: 1.75em;
  border-bottom: 1px solid {colors['outline']};
  padding-bottom: 0.3em;
}}

h2 {{
  font-size: 1.5em;
  border-bottom: 1px solid {colors['outline_variant']};
  padding-bottom: 0.3em;
}}

h3 {{ font-size: 1.25em; }}
h4 {{ font-size: 1.1em; }}

/* Paragraphs and text */
p {{
  margin: 0.75em 0;
}}

a {{
  color: {colors['primary']};
  text-decoration: none;
}}

a:hover {{
  color: {colors['primary_hover']};
  text-decoration: underline;
}}

/* Code */
code {{
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  font-size: 0.9em;
  padding: 0.2em 0.4em;
  background-color: {colors['surface_2']};
  border-radius: 4px;
}}

pre {{
  font-family: "Consolas", "Monaco", "Courier New", monospace;
  font-size: 0.9em;
  padding: 1em;
  background-color: {colors['surface_2']};
  border: 1px solid {colors['outline']};
  border-radius: 6px;
  overflow-x: auto;
}}

pre code {{
  padding: 0;
  background: none;
}}

/* Lists */
ul, ol {{
  margin: 0.75em 0;
  padding-left: 1.5em;
}}

li {{
  margin: 0.25em 0;
}}

/* Tables */
table {{
  width: 100%;
  border-collapse: collapse;
  margin: 1em 0;
}}

th, td {{
  padding: 0.5em 0.75em;
  text-align: left;
  border: 1px solid {colors['outline']};
}}

th {{
  background-color: {colors['surface_2']};
  font-weight: 600;
}}

tr:nth-child(even) {{
  background-color: {colors['surface_1']};
}}

/* Blockquote */
blockquote {{
  margin: 1em 0;
  padding: 0.5em 1em;
  border-left: 4px solid {colors['primary']};
  background-color: {colors['surface_1']};
  color: {colors['text_secondary']};
}}

/* Horizontal rule */
hr {{
  border: none;
  border-top: 1px solid {colors['outline']};
  margin: 1.5em 0;
}}

/* Selection */
::selection {{
  background-color: {colors['primary']};
  color: {colors['on_primary']};
}}

/* Scrollbar styling for webkit - matches globals.qss */
::-webkit-scrollbar {{
  width: 12px;
  height: 12px;
}}

::-webkit-scrollbar-track {{
  background-color: {colors['surface_2']};
  border-radius: 6px;
}}

::-webkit-scrollbar-thumb {{
  background-color: {colors['surface_4']};
  border-radius: 6px;
  min-height: 30px;
}}

::-webkit-scrollbar-thumb:hover {{
  background-color: {colors['primary']};
}}

::-webkit-scrollbar-thumb:active {{
  background-color: {colors['primary_pressed']};
}}

::-webkit-scrollbar-corner {{
  background-color: {colors['surface_2']};
}}
"""

    return styles

generate_css_variables(theme=None)

Generate CSS custom properties (variables) for the theme.

Parameters:

Name Type Description Default
theme Optional[str]

Theme to generate for (default: current theme).

None

Returns:

Type Description
str

CSS string with :root variables.

Source code in src\shared_services\rendering\documents\web_view_colors.py
def generate_css_variables(theme: Optional[str] = None) -> str:
    """
    Generate CSS custom properties (variables) for the theme.

    Args:
        theme: Theme to generate for (default: current theme).

    Returns:
        CSS string with :root variables.
    """
    use_theme = theme if theme else _current_theme
    colors = ColorSystem.get_colors(use_theme)

    css_vars = [
        ":root {",
        f"  --color-background: {colors['background']};",
        f"  --color-surface: {colors['surface']};",
        f"  --color-surface-1: {colors['surface_1']};",
        f"  --color-surface-2: {colors['surface_2']};",
        f"  --color-surface-3: {colors['surface_3']};",
        f"  --color-text-primary: {colors['text_primary']};",
        f"  --color-text-secondary: {colors['text_secondary']};",
        f"  --color-text-tertiary: {colors['text_tertiary']};",
        f"  --color-primary: {colors['primary']};",
        f"  --color-primary-hover: {colors['primary_hover']};",
        f"  --color-secondary: {colors['secondary']};",
        f"  --color-outline: {colors['outline']};",
        f"  --color-outline-variant: {colors['outline_variant']};",
        f"  --color-divider: {colors['divider']};",
        f"  --color-error: {colors['error']};",
        f"  --color-success: {colors['success']};",
        f"  --color-warning: {colors['warning']};",
        f"  --color-info: {colors['info']};",
        "}",
    ]

    return "\n".join(css_vars)

generate_theme_stylesheet(theme=None)

Generate complete theme stylesheet combining variables and base styles.

Parameters:

Name Type Description Default
theme Optional[str]

Theme to generate for (default: current theme).

None

Returns:

Type Description
str

Complete CSS string ready for injection.

Source code in src\shared_services\rendering\documents\web_view_colors.py
def generate_theme_stylesheet(theme: Optional[str] = None) -> str:
    """
    Generate complete theme stylesheet combining variables and base styles.

    Args:
        theme: Theme to generate for (default: current theme).

    Returns:
        Complete CSS string ready for injection.
    """
    css_vars = generate_css_variables(theme)
    base_styles = generate_base_styles(theme)
    return f"{css_vars}\n{base_styles}"

get_color(key, theme=None)

Get a color by ColorSystem key.

Parameters:

Name Type Description Default
key str

ColorSystem palette key.

required
theme Optional[str]

Optional theme override.

None

Returns:

Type Description
str

Hex color string.

Source code in src\shared_services\rendering\documents\web_view_colors.py
def get_color(key: str, theme: Optional[str] = None) -> str:
    """
    Get a color by ColorSystem key.

    Args:
        key: ColorSystem palette key.
        theme: Optional theme override.

    Returns:
        Hex color string.
    """
    use_theme = theme if theme else _current_theme
    colors = ColorSystem.get_colors(use_theme)
    return colors.get(key, colors["text_primary"])

get_configured_profile()

Get the configured WebEngine profile.

Convenience function that ensures configuration and returns the default profile.

Returns:

Type Description
QWebEngineProfile

The configured QWebEngineProfile.

Source code in src\shared_services\rendering\documents\profile_config.py
def get_configured_profile() -> QWebEngineProfile:
    """
    Get the configured WebEngine profile.

    Convenience function that ensures configuration and returns
    the default profile.

    Returns:
        The configured QWebEngineProfile.
    """
    return ProfileManager.get_profile()

get_theme()

Get the current theme.

Source code in src\shared_services\rendering\documents\web_view_colors.py
def get_theme() -> str:
    """Get the current theme."""
    return _current_theme

get_web_view_registry()

Get the singleton WebViewRegistry instance.

Returns:

Type Description
WebViewRegistry

The shared WebViewRegistry instance.

Source code in src\shared_services\rendering\documents\web_view_registry.py
def get_web_view_registry() -> WebViewRegistry:
    """
    Get the singleton WebViewRegistry instance.

    Returns:
        The shared WebViewRegistry instance.
    """
    return WebViewRegistry.instance()

refresh_all_views()

Refresh all registered views with current theme.

Call this if you need to force a refresh of all web views. Normally not needed as views update automatically on theme change.

Source code in src\shared_services\rendering\documents\api.py
def refresh_all_views() -> None:
    """
    Refresh all registered views with current theme.

    Call this if you need to force a refresh of all web views.
    Normally not needed as views update automatically on theme change.
    """
    registry = get_web_view_registry()
    registry.refresh_all()

render_html(view, html, custom_css=None)

Render HTML content to a view with theme styles.

Convenience function that sets HTML and registers for theme updates.

Parameters:

Name Type Description Default
view HtmlView

The HtmlView to render to.

required
html str

HTML content.

required
custom_css Optional[str]

Optional custom CSS.

None

Example::

render_html(view, "<p>Inhalt</p>", custom_css="p { margin: 10px; }")
Source code in src\shared_services\rendering\documents\api.py
def render_html(
    view: HtmlView,
    html: str,
    custom_css: Optional[str] = None,
) -> None:
    """
    Render HTML content to a view with theme styles.

    Convenience function that sets HTML and registers for theme updates.

    Args:
        view: The HtmlView to render to.
        html: HTML content.
        custom_css: Optional custom CSS.

    Example::

        render_html(view, "<p>Inhalt</p>", custom_css="p { margin: 10px; }")
    """
    view.set_html(html, custom_css=custom_css, register_for_theme=True)

set_theme(theme)

Set the current theme for web view colors.

Parameters:

Name Type Description Default
theme str

Theme name ("light" or "dark").

required
Source code in src\shared_services\rendering\documents\web_view_colors.py
def set_theme(theme: str) -> None:
    """
    Set the current theme for web view colors.

    Args:
        theme: Theme name ("light" or "dark").
    """
    global _current_theme
    if theme in ("light", "dark"):
        _current_theme = theme

unregister_view(view)

Unregister a view from theme updates.

Parameters:

Name Type Description Default
view HtmlView

The view to unregister.

required
Source code in src\shared_services\rendering\documents\api.py
def unregister_view(view: HtmlView) -> None:
    """
    Unregister a view from theme updates.

    Args:
        view: The view to unregister.
    """
    registry = get_web_view_registry()
    registry.unregister(view)