Skip to content

Commit c4a325d

Browse files
aksOpsclaude
andcommitted
fix: UI polish — object display, zero-value modals, dark theme, map sizing, console default
- Dashboard: fix [object object] for REST endpoints — flattenToRecord handles nested API responses. Zero-value stats don't open modal. - Dark theme: lighter grayish black (#1f1f23/#27272b/#18181b) instead of deep purple-black - Codebase Map: negative margin to fill full viewport, no card wrapper - MCP Console: first tool selected by default on load Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b2b1c3a commit c4a325d

5 files changed

Lines changed: 52 additions & 28 deletions

File tree

src/main/frontend/src/components/AppLayout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default function AppLayout() {
3939
position: 'sticky',
4040
top: 0,
4141
zIndex: 100,
42-
borderBottom: isDark ? '1px solid #2d2d4a' : '1px solid #f0f0f0',
42+
borderBottom: isDark ? '1px solid #3f3f46' : '1px solid #f0f0f0',
4343
}}
4444
>
4545
<Typography.Title

src/main/frontend/src/main.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ function ThemedApp() {
2828
wireframe: false,
2929
// Dark mode refinements
3030
...(isDark ? {
31-
colorBgContainer: '#1a1a2e',
32-
colorBgElevated: '#1f1f38',
33-
colorBgLayout: '#0f0f1a',
34-
colorBorder: '#2d2d4a',
35-
colorBorderSecondary: '#252542',
31+
colorBgContainer: '#1f1f23',
32+
colorBgElevated: '#27272b',
33+
colorBgLayout: '#18181b',
34+
colorBorder: '#3f3f46',
35+
colorBorderSecondary: '#34343a',
3636
} : {
3737
colorBgContainer: '#ffffff',
3838
colorBgElevated: '#ffffff',
@@ -41,8 +41,8 @@ function ThemedApp() {
4141
},
4242
components: {
4343
Table: {
44-
headerBg: isDark ? '#1f1f38' : '#fafafa',
45-
rowHoverBg: isDark ? '#252542' : '#f0f0ff',
44+
headerBg: isDark ? '#27272b' : '#fafafa',
45+
rowHoverBg: isDark ? '#2d2d33' : '#f0f0ff',
4646
},
4747
Card: {
4848
paddingLG: 20,

src/main/frontend/src/pages/CodebaseMap.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export default function CodebaseMap() {
202202
}
203203

204204
return (
205-
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 80px)' }}>
205+
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 96px)', margin: '-16px -24px', padding: '8px 16px 0' }}>
206206
{/* Top bar: title + filter */}
207207
<div style={{
208208
display: 'flex',

src/main/frontend/src/pages/Dashboard.tsx

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,29 @@ import { useApi } from '@/hooks/useApi';
1414
import { api } from '@/lib/api';
1515
import type { StatsResponse } from '@/types/api';
1616

17+
/** Flatten a value into a Record<string, number>. Handles nested objects, numbers, arrays. */
18+
function flattenToRecord(val: unknown): Record<string, number> {
19+
if (!val || typeof val !== 'object') return {};
20+
const result: Record<string, number> = {};
21+
for (const [k, v] of Object.entries(val as Record<string, unknown>)) {
22+
if (typeof v === 'number') {
23+
result[k] = v;
24+
} else if (typeof v === 'object' && v !== null && !Array.isArray(v)) {
25+
// Nested object — flatten one level
26+
for (const [k2, v2] of Object.entries(v as Record<string, unknown>)) {
27+
if (typeof v2 === 'number') {
28+
result[`${k}/${k2}`] = v2;
29+
}
30+
}
31+
}
32+
}
33+
return result;
34+
}
35+
36+
function sumValues(rec: Record<string, number>): number {
37+
return Object.values(rec).reduce((a, b) => a + b, 0);
38+
}
39+
1740
interface StatCardProps {
1841
title: string;
1942
value: number | string;
@@ -25,21 +48,24 @@ interface StatCardProps {
2548
function StatCard({ title, value, icon, detail, detailTitle }: StatCardProps) {
2649
const [open, setOpen] = useState(false);
2750

28-
const tableData = detail
29-
? Object.entries(detail)
51+
// Only allow click if detail has entries and total > 0
52+
const hasDetail = detail && Object.keys(detail).length > 0 && sumValues(detail) > 0;
53+
54+
const tableData = hasDetail
55+
? Object.entries(detail!)
3056
.sort((a, b) => b[1] - a[1])
3157
.map(([name, count]) => ({ key: name, name, count }))
3258
: [];
3359

3460
return (
3561
<>
3662
<Card
37-
hoverable={!!detail}
38-
onClick={() => detail && setOpen(true)}
39-
style={{ cursor: detail ? 'pointer' : 'default' }}
63+
hoverable={!!hasDetail}
64+
onClick={() => hasDetail && setOpen(true)}
65+
style={{ cursor: hasDetail ? 'pointer' : 'default' }}
4066
>
4167
<Statistic title={title} value={value} prefix={icon} />
42-
{detail && (
68+
{hasDetail && (
4369
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
4470
Click for breakdown
4571
</Typography.Text>
@@ -76,9 +102,9 @@ function isComputedStats(s: StatsResponse): s is StatsResponse & {
76102
graph: { nodes: number; edges: number; files: number };
77103
languages: Record<string, number>;
78104
frameworks: Record<string, number>;
79-
connections?: { rest?: Record<string, number> };
80-
auth?: Record<string, number>;
81-
architecture?: Record<string, number>;
105+
connections?: unknown;
106+
auth?: unknown;
107+
architecture?: unknown;
82108
} {
83109
return 'graph' in s;
84110
}
@@ -95,14 +121,13 @@ export default function Dashboard() {
95121
const nodeCount = computed?.graph?.nodes ?? queryStats?.node_count ?? 0;
96122
const edgeCount = computed?.graph?.edges ?? queryStats?.edge_count ?? 0;
97123
const fileCount = computed?.graph?.files ?? 0;
98-
const languages = computed?.languages ?? {};
124+
const languages = flattenToRecord(computed?.languages);
99125
const langCount = Object.keys(languages).length;
100-
const frameworks = computed?.frameworks ?? {};
101-
const connections = computed?.connections?.rest ?? {};
102-
const auth = computed?.auth ?? {};
103-
const architecture = computed?.architecture ?? {};
126+
const frameworks = flattenToRecord(computed?.frameworks);
127+
const connections = flattenToRecord(computed?.connections);
128+
const auth = flattenToRecord(computed?.auth);
129+
const architecture = flattenToRecord(computed?.architecture);
104130

105-
// Build node kind breakdown from kinds API
106131
const nodeKindBreakdown: Record<string, number> = {};
107132
if (kinds?.kinds) {
108133
for (const k of kinds.kinds) {
@@ -167,7 +192,7 @@ export default function Dashboard() {
167192
<Col xs={12} sm={8} md={6}>
168193
<StatCard
169194
title="REST Endpoints"
170-
value={Object.values(connections).reduce((a, b) => a + b, 0)}
195+
value={sumValues(connections)}
171196
icon={<ApiOutlined />}
172197
detail={connections}
173198
detailTitle="REST Endpoints by Method"
@@ -176,7 +201,7 @@ export default function Dashboard() {
176201
<Col xs={12} sm={8} md={6}>
177202
<StatCard
178203
title="Auth Guards"
179-
value={Object.values(auth).reduce((a, b) => a + b, 0)}
204+
value={sumValues(auth)}
180205
icon={<SafetyOutlined />}
181206
detail={auth}
182207
detailTitle="Auth Patterns"
@@ -193,7 +218,6 @@ export default function Dashboard() {
193218
</Col>
194219
</Row>
195220

196-
{/* Frameworks as tags for quick glance */}
197221
{Object.keys(frameworks).length > 0 && (
198222
<Card title="Detected Frameworks" style={{ marginTop: 16 }} size="small">
199223
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>

src/main/frontend/src/pages/McpConsole.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function countResults(json: unknown): number | null {
5454
}
5555

5656
export default function McpConsole() {
57-
const [selectedTool, setSelectedTool] = useState<McpTool | null>(null);
57+
const [selectedTool, setSelectedTool] = useState<McpTool | null>(TOOLS[0] ?? null);
5858
const [response, setResponse] = useState<string>('');
5959
const [status, setStatus] = useState<number | null>(null);
6060
const [duration, setDuration] = useState<number | null>(null);

0 commit comments

Comments
 (0)