Skip to content

Commit 963e692

Browse files
aksOpsclaude
andcommitted
fix: flow view uses iframe for full-page HTML, add API docs links to header
- Flow view: serve full Cytoscape HTML at /flow-embed, embed via iframe (full DOCTYPE page cannot be embedded as ui.html fragment) - Header: add API Docs and Stats JSON quick-link buttons - Header: show backend badge alongside node/edge counts - Separators between header sections for cleaner layout Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent efda176 commit 963e692

2 files changed

Lines changed: 71 additions & 35 deletions

File tree

src/osscodeiq/server/ui/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def index():
2626
with ui.row().classes("items-center gap-3"):
2727
ui.icon("hub").style(f"color: {BRAND_COLOR}; font-size: 28px")
2828
ui.label("OSSCodeIQ").classes("text-lg font-bold")
29-
with ui.row().classes("items-center gap-2"):
29+
with ui.row().classes("items-center gap-3"):
30+
# Stats badges
3031
try:
3132
stats = service.get_stats()
3233
ui.badge(
@@ -35,10 +36,31 @@ def index():
3536
ui.badge(
3637
f"{stats.get('total_edges', 0):,} edges"
3738
).props("color=positive outline")
39+
ui.badge(
40+
stats.get("backend", "?")
41+
).props("outline")
3842
except Exception: # noqa: BLE001
3943
ui.badge("stats unavailable").props(
4044
"color=warning outline"
4145
)
46+
47+
ui.separator().props("vertical")
48+
49+
# Quick links
50+
ui.button(
51+
"API Docs",
52+
icon="description",
53+
on_click=lambda: ui.navigate.to("/docs", new_tab=True),
54+
).props("flat dense no-caps")
55+
ui.button(
56+
"Stats JSON",
57+
icon="data_object",
58+
on_click=lambda: ui.navigate.to("/api/stats", new_tab=True),
59+
).props("flat dense no-caps")
60+
61+
ui.separator().props("vertical")
62+
63+
# Theme toggles
4264
ui.button(
4365
icon="light_mode",
4466
on_click=lambda: dark.set_value(False),
Lines changed: 48 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,59 @@
1-
"""Flow View — wraps existing Cytoscape flow visualization."""
1+
"""Flow View — serves existing Cytoscape flow visualization via iframe."""
22
from __future__ import annotations
33

4-
from nicegui import ui
4+
from typing import Any
55

6+
from nicegui import app, ui
67

7-
def create_flow_page(service) -> None:
8+
9+
def create_flow_page(service: Any) -> None:
810
"""Build the Flow tab inside a NiceGUI page.
911
10-
Attempts to generate an overview flow diagram as HTML via the service.
11-
Falls back to a placeholder when no analysis data is available.
12+
The flow engine generates a full self-contained HTML page (DOCTYPE + Cytoscape.js
13+
+ vendor JS). This cannot be embedded as a fragment via ui.html(). Instead, we
14+
serve it at a dedicated route and embed it in an iframe.
1215
"""
13-
with ui.element("div").classes("max-w-7xl mx-auto px-4 w-full"):
14-
try:
15-
result = service.generate_flow("overview", "html")
16-
17-
# generate_flow returns a dict; the HTML is in the "content" key
18-
html_content: str | None = None
19-
if isinstance(result, dict):
20-
html_content = result.get("content") or result.get("html")
21-
elif isinstance(result, str):
22-
html_content = result
23-
24-
if html_content:
25-
ui.html(html_content).classes("w-full")
26-
else:
27-
_show_placeholder()
28-
29-
except Exception: # noqa: BLE001
30-
_show_placeholder()
16+
_flow_html: str | None = None
17+
18+
try:
19+
result = service.generate_flow("overview", "html")
20+
if isinstance(result, str) and result.strip().startswith("<!"):
21+
_flow_html = result
22+
elif isinstance(result, dict):
23+
content = result.get("content") or result.get("html", "")
24+
if content and content.strip().startswith("<!"):
25+
_flow_html = content
26+
except Exception: # noqa: BLE001
27+
_flow_html = None
28+
29+
if _flow_html:
30+
# Register a dedicated route to serve the full-page HTML
31+
@app.get("/flow-embed", include_in_schema=False)
32+
async def _serve_flow_html():
33+
from starlette.responses import HTMLResponse
34+
return HTMLResponse(_flow_html)
35+
36+
with ui.column().classes("w-full h-full"):
37+
ui.html(
38+
'<iframe src="/flow-embed" '
39+
'style="width:100%;height:calc(100vh - 160px);border:none;" '
40+
'loading="lazy"></iframe>'
41+
)
42+
else:
43+
_show_placeholder()
3144

3245

3346
def _show_placeholder() -> None:
3447
"""Show a professional centered placeholder when no flow data is available."""
35-
with ui.card().classes("w-full max-w-md mx-auto mt-16"):
36-
with ui.card_section().classes("items-center text-center"):
37-
with ui.column().classes("items-center gap-3 py-8"):
38-
ui.icon("account_tree", size="64px").classes("opacity-30")
39-
ui.label("No flow data available").classes(
40-
"text-xl font-medium opacity-70"
41-
)
42-
ui.label(
43-
"Run 'osscodeiq analyze <path>' to scan a codebase, "
44-
"then refresh this page."
45-
).classes("text-sm opacity-50 max-w-xs text-center")
48+
with ui.column().classes("w-full items-center justify-center py-20"):
49+
with ui.card().classes("max-w-md text-center"):
50+
with ui.card_section():
51+
with ui.column().classes("items-center gap-4 py-8"):
52+
ui.icon("account_tree", size="64px").classes("opacity-30")
53+
ui.label("No flow data available").classes(
54+
"text-xl font-medium opacity-70"
55+
)
56+
ui.label(
57+
"Run 'osscodeiq analyze <path>' to generate flow diagrams, "
58+
"then refresh this page."
59+
).classes("text-sm opacity-50")

0 commit comments

Comments
 (0)