Skip to content

Commit 3e45072

Browse files
committed
fix: address code review feedback
CodebaseIntelligence.tsx: - Fix guard to work with deps/nodes instead of requiring metrics - Fix startHere to find critical file that's also entry point - Fix totalFunctions to only use real data, show N/A when missing - Fix HealthIndicator to handle 0 values (not treat as falsy) DashboardLayout.tsx: - Wrap localStorage operations in try/catch DashboardStats.tsx: - Rename 'Functions Indexed' to 'Files Indexed' (correct metric) TopNav.tsx: - Add aria-label to theme toggle for accessibility RepoList.tsx: - Change 'Functions' label to 'Files' (matches file_count) RepoSummaryCard.tsx: - Fix markdown asterisks - use JSX <strong> for repo name - Fix primaryLang to sort by dominant language value ResultCard.tsx: - Use resolvedTheme instead of theme for system theme support
1 parent 1cbf10a commit 3e45072

7 files changed

Lines changed: 65 additions & 32 deletions

File tree

frontend/src/components/CodebaseIntelligence.tsx

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ export function CodebaseIntelligence({ repo, apiKey, onTabChange }: CodebaseInte
3232

3333
// Derive intelligence from raw data
3434
const intelligence = useMemo(() => {
35-
if (!deps?.metrics) return null
36-
37-
const nodes = deps.nodes || []
38-
const edges = deps.edges || []
35+
const nodes = deps?.nodes || []
36+
const edges = deps?.edges || []
37+
38+
// Gate on deps existence or having graph data
39+
if (!deps && nodes.length === 0) return null
3940

4041
// Calculate in-degree and out-degree from edges
4142
const inDegree: Record<string, number> = {}
@@ -90,15 +91,21 @@ export function CodebaseIntelligence({ repo, apiKey, onTabChange }: CodebaseInte
9091
})
9192

9293
// Find "start here" file - the most critical file that's also an entry point
93-
const startHere = criticalFiles[0]?.file || entryPoints[0]?.file || null
94+
const criticalEntryPoint = criticalFiles.find(cf =>
95+
entryPoints.some(ep => ep.file === cf.file)
96+
)
97+
const startHere = criticalEntryPoint?.file || entryPoints[0]?.file || null
9498

9599
// Generate smart summary
96-
const totalFiles = deps.total_files || nodes.length
97-
const totalFunctions = style?.summary?.total_functions || repo.file_count || 0
100+
const totalFiles = deps?.total_files || nodes.length
101+
const totalFunctions: number | null = style?.summary?.total_functions ?? null
98102

99-
let sizeDesc = 'compact'
100-
if (totalFunctions > 1000) sizeDesc = 'large'
101-
else if (totalFunctions > 200) sizeDesc = 'medium-sized'
103+
let sizeDesc: string | null = null
104+
if (typeof totalFunctions === 'number') {
105+
if (totalFunctions > 1000) sizeDesc = 'large'
106+
else if (totalFunctions > 200) sizeDesc = 'medium-sized'
107+
else sizeDesc = 'compact'
108+
}
102109

103110
// Detect patterns
104111
const hasMiddleware = nodes.some((n: any) =>
@@ -168,9 +175,11 @@ export function CodebaseIntelligence({ repo, apiKey, onTabChange }: CodebaseInte
168175
</h3>
169176

170177
<p className="text-foreground leading-relaxed mb-4">
171-
<strong>{repo.name}</strong> is a {intelligence.summary.size} {intelligence.summary.language} codebase
172-
with <span className="text-primary font-semibold">{intelligence.summary.totalFunctions.toLocaleString()}</span> functions
173-
across {intelligence.summary.totalFiles} files.
178+
<strong>{repo.name}</strong> is a {intelligence.summary.size ? `${intelligence.summary.size} ` : ''}{intelligence.summary.language} codebase
179+
{intelligence.summary.totalFunctions != null ? (
180+
<> with <span className="text-primary font-semibold">{intelligence.summary.totalFunctions.toLocaleString()}</span> functions</>
181+
) : null}
182+
{' '}across {intelligence.summary.totalFiles} files.
174183
{intelligence.summary.pattern && (
175184
<> Uses {intelligence.summary.pattern}.</>
176185
)}
@@ -367,8 +376,8 @@ export function CodebaseIntelligence({ repo, apiKey, onTabChange }: CodebaseInte
367376
)
368377
}
369378

370-
function HealthIndicator({ label, value, threshold }: { label: string, value: string | null, threshold: number }) {
371-
if (!value) {
379+
function HealthIndicator({ label, value, threshold }: { label: string, value: string | number | null, threshold: number }) {
380+
if (value == null) {
372381
return (
373382
<div className="p-3 bg-muted/50 rounded-lg">
374383
<p className="text-[10px] text-muted-foreground uppercase tracking-wide mb-1">{label}</p>
@@ -377,15 +386,16 @@ function HealthIndicator({ label, value, threshold }: { label: string, value: st
377386
)
378387
}
379388

380-
const numValue = parseFloat(value)
381-
const isGood = numValue >= threshold
389+
const numValue = typeof value === 'number' ? value : parseFloat(value)
390+
const isGood = !isNaN(numValue) && numValue >= threshold
391+
const displayValue = typeof value === 'number' ? `${value}%` : value
382392

383393
return (
384394
<div className="p-3 bg-muted/50 rounded-lg">
385395
<p className="text-[10px] text-muted-foreground uppercase tracking-wide mb-1">{label}</p>
386396
<p className={`text-sm font-semibold flex items-center gap-1 ${isGood ? 'text-green-600 dark:text-green-400' : 'text-foreground'}`}>
387397
{isGood && <CheckCircle2 className="w-3.5 h-3.5" />}
388-
{value}
398+
{displayValue}
389399
</p>
390400
</div>
391401
)

frontend/src/components/RepoList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const RepoCard = ({ repo, index, onSelect }: {
8383
{/* Stats */}
8484
<div className="pt-4 border-t border-border">
8585
<div className="flex items-center justify-between">
86-
<span className="text-sm text-muted-foreground">Functions</span>
86+
<span className="text-sm text-muted-foreground">Files</span>
8787
<span className="text-2xl font-bold text-primary">
8888
{(repo.file_count || 0).toLocaleString()}
8989
</span>

frontend/src/components/RepoSummaryCard.tsx

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ export function RepoSummaryCard({ repo, apiKey }: RepoSummaryCardProps) {
5353
<span className="text-[10px] px-1.5 py-0.5 bg-primary/20 text-primary rounded font-normal">Auto-generated</span>
5454
</h3>
5555

56-
<p className="text-foreground leading-relaxed mb-4">{summary.main}</p>
56+
<p className="text-foreground leading-relaxed mb-4">
57+
<strong>{repo.name}</strong> {summary.main}
58+
</p>
5759

5860
{/* Quick Stats Pills */}
5961
<div className="flex flex-wrap gap-2">
@@ -99,7 +101,19 @@ function generateSummary(repo: Repository, insights: any, style: any) {
99101
const fileCount = insights?.total_files || repo.file_count || 0
100102
const functionCount = style?.summary?.total_functions || insights?.total_functions || 0
101103
const languages = insights?.languages || style?.language_distribution || {}
102-
const primaryLang = Object.keys(languages)[0] || 'Unknown'
104+
105+
// Get primary language by sorting entries by value descending
106+
const langEntries = Object.entries(languages)
107+
let primaryLang = 'Unknown'
108+
if (langEntries.length > 0) {
109+
const sorted = langEntries.sort((a, b) => {
110+
const valA = typeof a[1] === 'number' ? a[1] : (a[1] as any)?.percentage ?? (a[1] as any)?.count ?? 0
111+
const valB = typeof b[1] === 'number' ? b[1] : (b[1] as any)?.percentage ?? (b[1] as any)?.count ?? 0
112+
return Number(valB) - Number(valA)
113+
})
114+
primaryLang = sorted[0][0]
115+
}
116+
103117
const asyncAdoption = style?.summary?.async_adoption || null
104118
const typeHints = style?.summary?.type_hints_usage || null
105119

@@ -125,8 +139,8 @@ function generateSummary(repo: Repository, insights: any, style: any) {
125139
stats.push({ icon: Code2, label: 'Async', value: asyncAdoption })
126140
}
127141

128-
// Generate main summary text
129-
let main = `**${repo.name}** is a `
142+
// Generate main summary text (repo name rendered separately in JSX)
143+
let main = `is a `
130144

131145
if (functionCount > 500) main += 'large '
132146
else if (functionCount > 100) main += 'medium-sized '

frontend/src/components/dashboard/DashboardLayout.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,21 @@ export function DashboardLayout({ children }: DashboardLayoutProps) {
1717
const { theme } = useTheme()
1818

1919
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
20-
const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY)
21-
return stored ? JSON.parse(stored) : false
20+
try {
21+
const stored = localStorage.getItem(SIDEBAR_STORAGE_KEY)
22+
return stored ? JSON.parse(stored) : false
23+
} catch {
24+
return false
25+
}
2226
})
2327
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false)
2428

2529
useEffect(() => {
26-
localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(sidebarCollapsed))
30+
try {
31+
localStorage.setItem(SIDEBAR_STORAGE_KEY, JSON.stringify(sidebarCollapsed))
32+
} catch {
33+
// Ignore storage errors
34+
}
2735
}, [sidebarCollapsed])
2836

2937
useKeyboardShortcut(SHORTCUTS.COMMAND_PALETTE, () => {

frontend/src/components/dashboard/DashboardStats.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ const useAnimatedCounter = (end: number, duration: number = 1200) => {
2929
export function DashboardStats({ repos }: DashboardStatsProps) {
3030
const totalRepos = repos.length
3131
const indexedRepos = repos.filter(r => r.status === 'indexed').length
32-
const totalFunctions = repos.reduce((acc, r) => acc + (r.file_count || 0), 0)
32+
const totalFiles = repos.reduce((acc, r) => acc + (r.file_count || 0), 0)
3333
const indexingCount = repos.filter(r => r.status === 'indexing' || r.status === 'cloning').length
3434

3535
const animatedTotal = useAnimatedCounter(totalRepos)
3636
const animatedIndexed = useAnimatedCounter(indexedRepos)
37-
const animatedFunctions = useAnimatedCounter(totalFunctions)
37+
const animatedFiles = useAnimatedCounter(totalFiles)
3838

3939
const stats = [
4040
{
@@ -56,8 +56,8 @@ export function DashboardStats({ repos }: DashboardStatsProps) {
5656
) : null,
5757
},
5858
{
59-
label: 'Functions Indexed',
60-
value: animatedFunctions,
59+
label: 'Files Indexed',
60+
value: animatedFiles,
6161
icon: Code2,
6262
suffix: '',
6363
format: true,

frontend/src/components/dashboard/TopNav.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function TopNav({ onToggleSidebar, sidebarCollapsed, onOpenCommandPalette
8282
size="icon"
8383
onClick={toggleTheme}
8484
className="text-muted-foreground hover:text-foreground"
85+
aria-label={theme === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}
8586
>
8687
{theme === 'dark' ? (
8788
<Sun className="w-5 h-5" />

frontend/src/components/search/ResultCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function ResultCard({ result, rank, isExpanded: initialExpanded = false,
1919
const [expanded, setExpanded] = useState(initialExpanded);
2020
const contentRef = useRef<HTMLDivElement>(null);
2121
const [contentHeight, setContentHeight] = useState<number | undefined>(initialExpanded ? undefined : 0);
22-
const { theme } = useTheme();
22+
const { resolvedTheme } = useTheme();
2323

2424
const matchPercent = Math.round(result.score * 100);
2525
const isTopResult = rank === 1;
@@ -90,7 +90,7 @@ export function ResultCard({ result, rank, isExpanded: initialExpanded = false,
9090
<div className="relative">
9191
<SyntaxHighlighter
9292
language={result.language || 'text'}
93-
style={theme === 'dark' ? oneDark : oneLight}
93+
style={resolvedTheme === 'dark' ? oneDark : oneLight}
9494
customStyle={{ margin: 0, borderRadius: 0, fontSize: '0.75rem', lineHeight: '1.6', padding: '1rem' }}
9595
showLineNumbers
9696
startingLineNumber={result.line_start}

0 commit comments

Comments
 (0)