33import asyncio
44from collections.abc import Awaitable, Callable
55import json
6+ import time
67from typing import Any
78
89import flet as ft
@@ -64,6 +65,22 @@ DEFAULT_LOGO_PATH = "aegis-manifesto.png"
6465DEFAULT_DARK_LOGO_PATH = "aegis-manifesto-dark.png"
6566
6667
68+ def _format_uptime(start: float) -> str:
69+ """Format elapsed time since start as a compact uptime string."""
70+ elapsed = int(time.monotonic() - start)
71+ minutes = elapsed // 60
72+ hours = minutes // 60
73+ days = hours // 24
74+ if days > 0:
75+ return f"Up {days}d {hours % 24}h"
76+ elif hours > 0:
77+ return f"Up {hours}h {minutes % 60}m"
78+ elif minutes > 0:
79+ return f"Up {minutes}m"
80+ else:
81+ return "Up <1m "
82+
83+
6784def _convert_component(comp_data: dict[str, Any]) -> ComponentStatus:
6885 " " " Recursively convert API component data to ComponentStatus. " " "
6986 try:
@@ -518,6 +535,29 @@ def create_frontend_app() -> Callable[[ft.Page], Awaitable[None]]:
518535 margin=ft.margin.only(right=20), # Space before theme button
519536 )
520537
538+ # Subtle uptime indicator - updated on each refresh cycle
539+ _session_start = time.monotonic()
540+ uptime_text = ft.Text(
541+ value=_format_uptime(_session_start),
542+ size=Theme.Typography.CAPTION,
543+ color=Theme.Colors.TEXT_SECONDARY,
544+ weight=ft.FontWeight.W_400,
545+ opacity=0.7,
546+ )
547+
548+ # Wrap health indicator + uptime in a column
549+ health_with_uptime = ft.Column(
550+ [
551+ health_indicator_container,
552+ ft.Container(
553+ content=uptime_text,
554+ alignment=ft.alignment.center,
555+ ),
556+ ],
557+ spacing=0,
558+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
559+ )
560+
521561 # Header with modern layout: logo left, status+controls right
522562 header = ft.Container(
523563 content=ft.Row(
@@ -528,7 +568,7 @@ def create_frontend_app() -> Callable[[ft.Page], Awaitable[None]]:
528568 ),
529569 ft.Row(
530570 [
531- health_indicator_container , # Health status
571+ health_with_uptime , # Health status + uptime
532572 # View toggle
533573 ft.Container(content=view_toggle_button, padding=10),
534574 # Theme toggle
@@ -846,9 +886,8 @@ def create_frontend_app() -> Callable[[ft.Page], Awaitable[None]]:
846886 components, create_component_card
847887 )
848888
849- # Keep worker modal cached permanently so SSE counters
850- # survive close/reopen cycles without resetting.
851- # Other modals can be pruned when closed.
889+ # Update uptime display
890+ uptime_text.value = _format_uptime(_session_start)
852891
853892 # Safe page update - check connection first
854893 if dashboard._is_page_connected():
0 commit comments