Skip to content

Commit 46e7121

Browse files
fix(gui): resolve multiple UI issues
- Fix template description extraction to skip separator lines (=====) - Make config editor template dropdown consistent with other buttons - Make network topology viewer responsive using ResizeObserver - Remove sparkles icon from Take a Tour button - Fix white screen when viewing nested sub-modules like RL Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6e2663c commit 46e7121

5 files changed

Lines changed: 89 additions & 50 deletions

File tree

frontend/src/components/topology/NetworkGraph.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { useState, useRef, useCallback, useMemo } from 'react'
1+
import { useState, useRef, useCallback, useMemo, useEffect } from 'react'
22
import type { TopologyNode, TopologyLink } from '@/api/types'
33

44
interface NetworkGraphProps {
55
nodes: TopologyNode[]
66
links: TopologyLink[]
7-
width?: number
8-
height?: number
97
onNodeClick?: (node: TopologyNode) => void
108
onLinkClick?: (link: TopologyLink) => void
119
}
@@ -30,12 +28,12 @@ function getUtilizationColor(utilization: number): string {
3028
export function NetworkGraph({
3129
nodes,
3230
links,
33-
width = 800,
34-
height = 600,
3531
onNodeClick,
3632
onLinkClick,
3733
}: NetworkGraphProps) {
34+
const containerRef = useRef<HTMLDivElement>(null)
3835
const svgRef = useRef<SVGSVGElement>(null)
36+
const [dimensions, setDimensions] = useState({ width: 800, height: 600 })
3937
const [tooltip, setTooltip] = useState<TooltipState>({
4038
visible: false,
4139
x: 0,
@@ -46,6 +44,26 @@ export function NetworkGraph({
4644
const [isPanning, setIsPanning] = useState(false)
4745
const [panStart, setPanStart] = useState({ x: 0, y: 0 })
4846

47+
// Track container size for responsiveness
48+
useEffect(() => {
49+
const container = containerRef.current
50+
if (!container) return
51+
52+
const updateDimensions = () => {
53+
setDimensions({
54+
width: container.clientWidth,
55+
height: container.clientHeight,
56+
})
57+
}
58+
59+
updateDimensions()
60+
61+
const resizeObserver = new ResizeObserver(updateDimensions)
62+
resizeObserver.observe(container)
63+
64+
return () => resizeObserver.disconnect()
65+
}, [])
66+
4967
// Create a map for quick node lookup
5068
const nodeMap = useMemo(() => {
5169
const map = new Map<string, TopologyNode>()
@@ -129,7 +147,7 @@ export function NetworkGraph({
129147
}, [])
130148

131149
return (
132-
<div className="relative">
150+
<div ref={containerRef} className="relative h-full w-full">
133151
{/* Controls */}
134152
<div className="absolute right-2 top-2 z-10 flex gap-1">
135153
<button
@@ -168,10 +186,10 @@ export function NetworkGraph({
168186
{/* SVG Canvas */}
169187
<svg
170188
ref={svgRef}
171-
width={width}
172-
height={height}
189+
width={dimensions.width}
190+
height={dimensions.height}
173191
viewBox={`${viewBox.minX} ${viewBox.minY} ${viewBox.width} ${viewBox.height}`}
174-
className="cursor-grab rounded-lg border border-gray-200 bg-white active:cursor-grabbing dark:border-gray-700 dark:bg-gray-800"
192+
className="cursor-grab bg-white active:cursor-grabbing dark:bg-gray-800"
175193
onMouseDown={handleMouseDown}
176194
onMouseMove={handleMouseMove}
177195
onMouseUp={handleMouseUp}

frontend/src/pages/CodebaseExplorerPage.tsx

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,10 @@ import {
1212
Database,
1313
Layers,
1414
Terminal,
15-
Brain,
16-
Shield,
1715
Cog,
1816
BookOpen,
19-
Play,
2017
ArrowLeft,
21-
ExternalLink,
2218
ChevronDown,
23-
Sparkles,
2419
} from 'lucide-react'
2520
import Editor from '@monaco-editor/react'
2621
import { codebaseApi } from '@/api/client'
@@ -151,10 +146,31 @@ export function CodebaseExplorerPage() {
151146
enabled: !!selectedFile,
152147
})
153148

154-
// Get module node from tree
155-
const getModuleNode = useCallback((moduleName: string): ModuleNode | null => {
149+
// Get module node from tree - supports nested paths like "modules/rl"
150+
const getModuleNode = useCallback((modulePath: string): ModuleNode | null => {
156151
if (!tree) return null
157-
return tree.root.children.find(c => c.name === moduleName) || null
152+
153+
// Helper to recursively find a node by its full path
154+
const findNode = (node: ModuleNode, targetPath: string): ModuleNode | null => {
155+
const fullPath = node.path.replace('fusion/', '')
156+
if (fullPath === targetPath) return node
157+
for (const child of node.children) {
158+
const found = findNode(child, targetPath)
159+
if (found) return found
160+
}
161+
return null
162+
}
163+
164+
// First try direct child lookup for top-level modules
165+
const directChild = tree.root.children.find(c => c.name === modulePath)
166+
if (directChild) return directChild
167+
168+
// Otherwise search recursively for nested paths
169+
for (const child of tree.root.children) {
170+
const found = findNode(child, modulePath)
171+
if (found) return found
172+
}
173+
return null
158174
}, [tree])
159175

160176
const selectedModuleNode = selectedModule ? getModuleNode(selectedModule) : null
@@ -289,7 +305,6 @@ export function CodebaseExplorerPage() {
289305
onClick={startTour}
290306
className="flex items-center gap-2 rounded-lg bg-fusion-600 px-4 py-2 text-sm font-medium text-white hover:bg-fusion-700"
291307
>
292-
<Sparkles className="h-4 w-4" />
293308
Take a Tour
294309
</button>
295310
)}
@@ -437,29 +452,35 @@ export function CodebaseExplorerPage() {
437452
)}
438453

439454
{/* Module View */}
440-
{viewMode === 'module' && selectedModuleNode && moduleInfo && (
455+
{viewMode === 'module' && selectedModuleNode && (
441456
<div>
442457
{/* Module Header Card */}
443-
<div className={`mb-6 rounded-xl border-2 p-6 ${moduleInfo.bgColor}`}>
458+
<div className={`mb-6 rounded-xl border-2 p-6 ${moduleInfo?.bgColor || 'bg-gray-50 dark:bg-gray-800 border-gray-200 dark:border-gray-700'}`}>
444459
<div className="flex items-start gap-4">
445-
<moduleInfo.icon className={`h-12 w-12 ${moduleInfo.color}`} />
460+
{moduleInfo ? (
461+
<moduleInfo.icon className={`h-12 w-12 ${moduleInfo.color}`} />
462+
) : (
463+
<Layers className="h-12 w-12 text-gray-600" />
464+
)}
446465
<div className="flex-1">
447466
<h2 className="text-xl font-bold text-gray-900 dark:text-gray-100">
448-
{selectedModule}
467+
{selectedModuleNode.name}
449468
</h2>
450469
<p className="mt-1 text-gray-600 dark:text-gray-300">
451-
{moduleInfo.description}
470+
{moduleInfo?.description || selectedModuleNode.description || `Explore the ${selectedModuleNode.name} module`}
452471
</p>
453-
<div className="mt-3 flex flex-wrap gap-2">
454-
{moduleInfo.highlights.map((h) => (
455-
<span
456-
key={h}
457-
className="rounded-full bg-white/80 px-3 py-1 text-sm font-medium text-gray-700 dark:bg-gray-800/80 dark:text-gray-300"
458-
>
459-
{h}
460-
</span>
461-
))}
462-
</div>
472+
{moduleInfo?.highlights && (
473+
<div className="mt-3 flex flex-wrap gap-2">
474+
{moduleInfo.highlights.map((h) => (
475+
<span
476+
key={h}
477+
className="rounded-full bg-white/80 px-3 py-1 text-sm font-medium text-gray-700 dark:bg-gray-800/80 dark:text-gray-300"
478+
>
479+
{h}
480+
</span>
481+
))}
482+
</div>
483+
)}
463484
</div>
464485
</div>
465486
</div>

frontend/src/pages/ConfigEditorPage.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,10 @@ export function ConfigEditorPage() {
113113
<div className="relative" ref={dropdownRef}>
114114
<button
115115
onClick={() => setDropdownOpen(!dropdownOpen)}
116-
className="flex items-center gap-3 rounded-lg border border-gray-200 bg-white px-4 py-2.5 text-left shadow-sm transition-all hover:border-gray-300 focus:border-fusion-500 focus:outline-none focus:ring-2 focus:ring-fusion-500/20 dark:border-gray-600 dark:bg-gray-800 dark:hover:border-gray-500"
116+
className="flex items-center gap-2 rounded-lg border border-gray-200 bg-white px-4 py-2.5 text-sm font-medium text-gray-700 shadow-sm transition-all hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700"
117117
>
118-
<FileCode className="h-5 w-5 text-fusion-500" />
119-
<div className="min-w-[120px]">
120-
<div className="text-sm font-medium text-gray-900 dark:text-gray-100">
121-
{templateName}
122-
</div>
123-
<div className="text-xs text-gray-500 dark:text-gray-400">
124-
Template
125-
</div>
126-
</div>
118+
<FileCode className="h-4 w-4 text-fusion-500" />
119+
{templateName}
127120
<ChevronDown className={`h-4 w-4 text-gray-400 transition-transform ${dropdownOpen ? 'rotate-180' : ''}`} />
128121
</button>
129122

frontend/src/pages/TopologyPage.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@ export function TopologyPage() {
133133
<NetworkGraph
134134
nodes={topology.nodes}
135135
links={topology.links}
136-
width={800}
137-
height={550}
138136
onNodeClick={setSelectedNode}
139137
onLinkClick={setSelectedLink}
140138
/>

fusion/api/routes/configs.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,22 @@ def list_templates() -> TemplateListResponse:
3737

3838
templates = []
3939
for path in sorted(templates_dir.glob("*.ini")):
40-
# Extract description from first comment line if present
40+
# Extract description from comment lines, skipping separator lines
4141
description = None
4242
try:
4343
with open(path) as f:
44-
first_line = f.readline().strip()
45-
if first_line.startswith(";") or first_line.startswith("#"):
46-
description = first_line.lstrip(";# ").strip()
44+
for line in f:
45+
line = line.strip()
46+
if not line:
47+
continue
48+
if not (line.startswith(";") or line.startswith("#")):
49+
break
50+
# Strip comment markers and whitespace
51+
content = line.lstrip(";# ").strip()
52+
# Skip separator lines (all =, -, or similar)
53+
if content and not all(c in "=-~*#" for c in content):
54+
description = content
55+
break
4756
except OSError:
4857
pass
4958

@@ -74,7 +83,7 @@ def get_template(name: str) -> dict:
7483
try:
7584
content = template_path.read_text()
7685
except OSError as e:
77-
raise HTTPException(status_code=500, detail=f"Failed to read template: {e}")
86+
raise HTTPException(status_code=500, detail=f"Failed to read template: {e}") from e
7887

7988
return {"name": name, "content": content}
8089

0 commit comments

Comments
 (0)