diff --git a/.gitignore b/.gitignore index 56429ff..0e7ee9b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ docker-compose.override.yml # Generated by pytest used to login users .nextcloud_oauth_*.json +.playwright-mcp/ diff --git a/nextcloud_mcp_server/auth/userinfo_routes.py b/nextcloud_mcp_server/auth/userinfo_routes.py index 8d19fa1..9b9309e 100644 --- a/nextcloud_mcp_server/auth/userinfo_routes.py +++ b/nextcloud_mcp_server/auth/userinfo_routes.py @@ -160,7 +160,7 @@ async def vector_sync_status_fragment(request: Request) -> HTMLResponse: if not processing_status: return HTMLResponse( """ -
+

Vector sync not available

""" @@ -182,24 +182,23 @@ async def vector_sync_status_fragment(request: Request) -> HTMLResponse: else: status_badge = '✓ Idle' + # Return inner content only (container div is in initial page render) html = f""" -
-

Vector Sync Status

- - - - - - - - - - - - - -
Indexed Documents{indexed_count_str}
Pending Documents{pending_count_str}
Status{status_badge}
-
+

Vector Sync Status

+ + + + + + + + + + + + + +
Indexed Documents{indexed_count_str}
Pending Documents{pending_count_str}
Status{status_badge}
""" return HTMLResponse(html) @@ -577,8 +576,9 @@ async def user_info_html(request: Request) -> HTMLResponse: vector_status_html = "" if processing_status: # Use htmx to load and auto-refresh the status fragment + # Container div stays stable, only inner content updates every 10s vector_status_html = """ -
+

Loading vector sync status...

""" @@ -671,6 +671,7 @@ async def user_info_html(request: Request) -> HTMLResponse: border-radius: 8px; padding: 30px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); + min-height: calc(100vh - 200px); }} h1 {{ color: #0082c9; @@ -713,10 +714,15 @@ async def user_info_html(request: Request) -> HTMLResponse: border-bottom-color: #0082c9; }} - /* Tab content */ + /* Tab content - use grid to overlay panes */ .tab-content {{ padding: 20px 0; - min-height: 300px; + display: grid; + }} + + /* Tab panes - all occupy the same grid cell to overlay */ + .tab-pane {{ + grid-area: 1 / 1; }} /* Tables */ @@ -803,6 +809,18 @@ async def user_info_html(request: Request) -> HTMLResponse: padding-top: 20px; border-top: 1px solid #e0e0e0; }} + + /* Smooth htmx content swaps */ + .htmx-swapping {{ + opacity: 0; + transition: opacity 200ms ease-out; + }} + + /* Smooth htmx content settling */ + .htmx-settling {{ + opacity: 1; + transition: opacity 200ms ease-in; + }} @@ -846,7 +864,7 @@ async def user_info_html(request: Request) -> HTMLResponse:
-
+
{user_info_tab_html}
@@ -855,7 +873,7 @@ async def user_info_html(request: Request) -> HTMLResponse: if not show_vector_sync_tab else f''' -
+
{vector_sync_tab_html}
''' @@ -866,7 +884,7 @@ async def user_info_html(request: Request) -> HTMLResponse: if not show_webhooks_tab else f''' -
+
{webhooks_tab_html}
'''