feat(ui): Phase 6 — MCP Console & API Docs redesign#24
Conversation
- Install class-variance-authority, tailwindcss-animate, react-resizable-panels - Add shadcn/ui CSS variables to index.css with AD-6 color tokens: light mode (white/gray backgrounds, indigo/blue accents) dark mode (deep charcoal/navy, muted teal/indigo accents) - Update tailwind.config.ts with shadcn CSS var tokens and tailwindcss-animate plugin - Create src/lib/utils.ts with cn() utility (clsx + twMerge) - Add shadcn/ui Button and Separator components in src/components/ui/ - Rewrite Layout.tsx with 3-panel architecture: - Collapsible left sidebar with desktop collapse/expand and mobile slide-in - Resizable main content area via react-resizable-panels - Collapsible right details panel (hidden by default, opens on demand) - New header: logo, global search bar, theme toggle, profile placeholder - RightPanelContext for child views to open the details panel programmatically - Migrate ThemeToggle to shadcn/ui Button styling (inline in Layout) - All existing routes (Dashboard, Graph, Explorer, Console, API Docs) preserved - Build passes clean: 0 TypeScript errors Co-Authored-By: Paperclip <noreply@paperclip.ing>
- playwright.config.ts: cross-browser matrix (Chrome, Firefox, Edge, Safari) + three responsive breakpoint projects (1920/1440/768) - tests/e2e/navigation.spec.ts: layout shell, routing, dark/light mode persistence - tests/e2e/graph.spec.ts: G6 drill-down, breadcrumb, controls, node interactions - tests/e2e/accessibility.spec.ts: axe WCAG 2.1 AA audit + keyboard nav + ARIA - tests/e2e/performance.spec.ts: render time baselines (100/1K/10K nodes) - tests/e2e/search.spec.ts: global search, debounce, file tree filter - tests/e2e/mcp-console.spec.ts: all 39 MCP tools, command palette, param validation - tests/e2e/responsive.spec.ts: sidebar/graph/dashboard at all three breakpoints - tests/e2e/edge-cases.spec.ts: empty graph, single node, disconnected, deep hierarchy, API errors - tests/utils/test-helpers.ts: shared mocks, route helpers, render-time measurement - package.json: add @playwright/test, @axe-core/playwright, @types/node devDeps + test:e2e scripts - tsconfig.test.json: TS config for test files Execution requires Phases 1-6 (RAN-71 through RAN-77) to be done. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ture Adds a new read-only REST endpoint that transforms flat file paths from the graph into a hierarchical JSON tree, enabling the Project Explorer tree-view in the redesigned UI. - GraphStore.getFilePathsWithCounts(): Cypher query returns distinct filePaths with node counts ordered alphabetically - QueryService.getFileTree(Integer maxDepth): builds hierarchical tree with aggregate directory counts, dirs-first sorting, and optional depth limit; tree construction uses private TreeNode inner class - GraphController GET /api/file-tree: wires up endpoint with optional depth query param, follows read-only serving layer contract - Tests: 5 unit tests in QueryServiceTest covering tree construction, sorting, depth limiting, empty graphs, and determinism; 2 integration tests in GraphControllerTest via standalone MockMvc Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add FileTreeNode type and getFileTree() API method (GET /api/file-tree) - Add FileSelectionContext to propagate selected file/dir across views - Add ProjectFileTree component with expand/collapse, file icons, node count badges, inline search filter, keyboard navigation (arrow keys), and density progress bar - Wire ProjectFileTree into sidebar, replacing Phase 1 placeholder - Update CodeGraphView to show file-filter badge and filter drill-down nodes by selected file or directory from context - Add 13 Playwright E2E tests covering tree render, expand/collapse, search, keyboard nav, ARIA roles, and graph filter integration Closes RAN-73 Co-Authored-By: Paperclip <noreply@paperclip.ing>
…aDetector Kotlin, and ReactComponentDetector RENDERS scoping - TopicLinker: handle SENDS_TO/RECEIVES_FROM edges from TIBCO EMS, IBM MQ, and Azure Messaging detectors so enterprise messaging patterns get cross-linked - ConfigDefDetector: implement actual @value and @ConfigurationProperties detection (was advertised in description but unimplemented); also detect Kafka ConfigDef.define() as before; fix description to match reality - KafkaDetector: add "kotlin" to supported languages so Kotlin/Spring Kafka codebases get @KafkaListener and KafkaTemplate coverage - ReactComponentDetector: scope RENDERS edge detection to each component's body section (from its match position to the next component's position) to prevent false RENDERS edges from multi-component files All fixes include new tests. Targeted Java tests pass: TopicLinkerTest (6), ReactComponentDetectorTest (5), JavaDetectorsTest$ConfigDefTests (6), JavaDetectorsTest$KafkaTests (5). Note: frontend build fails on CodeGraphView.tsx TypeScript errors that are pre-existing in the working tree (not caused by this PR). Co-Authored-By: Paperclip <noreply@paperclip.ing>
… stats, caching - EnrichCommand: add FULLTEXT INDEX on CodeNode[label, fqn] during enrich - GraphStore.search(): rewrite both overloads to use db.index.fulltext.queryNodes instead of full node-store toLower CONTAINS scans - GraphStore.computeGraphStats(): collapse 3 separate transactions into one - GraphStore.findEndpointNeighborsBatch(): new single-query method replaces up to 50 individual findNeighbors() calls in findRelatedEndpoints() - QueryService.findRelatedEndpoints(): rewrite to use batch neighbor query, reducing N+1 (51 transactions) to 2 transactions per call - QueryService: add @Cacheable to consumersOf, callersOf, findComponentByFile, and findRelatedEndpoints (all read-only, safe to cache) - QueryServiceTest: add 3 tests for new findRelatedEndpoints batch behavior All 1475 tests pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…zation
- Install @antv/g6 v5 (code-split to vendor-g6 chunk, 411 KB gzip)
- Remove direct D3 usage from CodeGraphView; d3 retained as G6 peer dep
- Rewrite CodeGraphView.tsx with 3-level interactive drill-down:
- Level 0 (Landscape): service topology via GET /api/topology, force layout
- Level 1 (Module): nodes within a service, force layout
- Level 2 (Component): ego subgraph via GET /api/ego/{id}?radius=2, dagre layout
- Graph interactions: click highlights deps, dblclick drills in, right-click menu
- Graph controls toolbar: zoom in/out, fit-view, fullscreen, layout switcher,
node-type filter, re-center
- Plugins: minimap, tooltip with node details
- Breadcrumb navigation for drill-up
- Legend showing active node kinds
- Extract KIND_COLORS + getKindColor/getEdgeColor to shared graphConstants.ts
- Add topology/ego/typed-neighbors API methods to api.ts and types to types/api.ts
- Add vendor-g6 manual chunk to vite.config.ts for bundle splitting
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add NodeDetailPanel.tsx: persistent right panel with collapsible sections
- Node Header: name, kind badge (color-coded), FQN display
- Location: file path + line numbers
- Classification: layer badge, framework, module, annotations
- Statistics: inbound/outbound edge counts
- Inbound/Outbound Dependencies: grouped by EdgeKind, each item
navigates to that node's ego subgraph on click
- Source Preview: Monaco Editor (read-only), language auto-detected
from file extension, offset line numbers, dark/light theme
- Warnings: placeholder section for future static analysis
- Wire CodeGraphView.tsx to open right panel on node:click via
RightPanelContext.openPanel(), close on canvas:click
- handleShowDetails in context menu also opens the panel
- Dependency links in panel navigate via loadLevel2 ego subgraph
- Uses stable ref pattern (openPanelRef, closePanelRef, loadLevel2Ref)
so graph event handlers don't trigger graph rebuilds
- Build verified: TypeScript clean, vite build passes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add shadcn/ui Card, Badge, Progress primitives (ui/card.tsx, badge.tsx, progress.tsx) - StatsCards: migrate to shadcn Card, animated counters with ease-out-quart, clickable cards navigate to Explorer - FrameworkBadges: migrate to shadcn Badge with semantic colour variants per framework family - Dashboard: full responsive CSS Grid redesign - SectionCard wrapper with icon + tracking-widest titles - StatRow with Progress bar + optional click navigation to /explorer/:kind - ConnectionMetric tiles (REST, gRPC, WebSocket, Producers, Consumers) — all clickable - InfraSubSection for databases/messaging/cloud with per-category Progress colours - Auth section with percentage badges - All 7 stat categories (graph, languages, frameworks, infra, connections, auth, architecture) rendered - Responsive: 1→2→3 col grid, tablet-width (768px) minimum - Dark/light mode consistent via CSS custom properties Co-Authored-By: Paperclip <noreply@paperclip.ing>
1. ConfigDefDetector: move seenKeys to file scope to prevent duplicate config nodes when multiple classes in the same file reference the same @value or ConfigDef key. 2. ReactComponentDetector: tighten RENDERS filter from allDetected to self-only guard (!tag.equals(comp.name())). Sibling components in the same file now correctly create RENDERS edges (Header → Footer was previously silently dropped). 3. KafkaDetector: extend CLASS_RE to handle Kotlin modifiers (data class, internal class, abstract class, open class, sealed class, object). Previously only matched plain Java-style `class Foo`. 4. ConfigDefDetector: add receiver discriminator to .define() call matching. Only flags calls where the scope references "ConfigDef" or "CONFIG" — prevents false positives from unrelated .define() calls in files that happen to import ConfigDef. 5. TopicLinker: add determinism test covering multi-sender/multi-receiver SENDS_TO/RECEIVES_FROM scenarios (sorted iteration requirement). All 108 targeted Java tests pass (0 failures). Co-Authored-By: Paperclip <noreply@paperclip.ing>
This reverts commit 695344c.
…Phase 6 Co-Authored-By: Paperclip <noreply@paperclip.ing>
…rimitives Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…e params, history - 7-category Accordion (type=multiple, expandable) - Command palette (Cmd+K / Ctrl+K) with fuzzy search - Inline param forms per tool with required validation - Shared response panel with status/duration/count badges - Execution history (last 10, click to restore) - data-testid attributes matching e2e test expectations Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
| {/* Subtle gradient accent top-right */} | ||
| <div className={cn( | ||
| 'absolute top-0 right-0 w-20 h-20 rounded-full blur-2xl opacity-10', | ||
| cfg.iconBg.replace('bg-', 'bg-').replace('/10', '/40') |
Check warning
Code scanning / CodeQL
Replacement of a substring with itself Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 months ago
In general, to fix this class of issue you remove or correct any string.replace calls where the search string and replacement string are identical, ensuring that every replacement either has an intended effect or is removed to avoid confusion. Here, the highlighted call cfg.iconBg.replace('bg-', 'bg-') can safely be removed, leaving only the meaningful /10 → /40 transformation. This preserves existing behavior (since the bg- replacement had no effect) while making the code clearer and eliminating the CodeQL finding.
Concretely, in src/main/frontend/src/components/StatsCards.tsx, on line 125, update the expression inside the cn(...) call so that it no longer performs the no-op replace('bg-', 'bg-'). The resulting line should simply call .replace('/10', '/40') on cfg.iconBg. No new imports, methods, or other definitions are needed.
| @@ -122,7 +122,7 @@ | ||
| {/* Subtle gradient accent top-right */} | ||
| <div className={cn( | ||
| 'absolute top-0 right-0 w-20 h-20 rounded-full blur-2xl opacity-10', | ||
| cfg.iconBg.replace('bg-', 'bg-').replace('/10', '/40') | ||
| cfg.iconBg.replace('/10', '/40') | ||
| )} /> | ||
|
|
||
| <CardContent className="p-4"> |
…onditions, leaks Resolves all blocking and high-priority findings from Principal Engineer review (RAN-79): BLOCKING: - Remove api.analyze() and postJson() from api.ts — violates read-only serving contract per CLAUDE.md. Remote serve mode has no source access. AnalyzeResponse type import also removed. HIGH: - api.ts readFile: add r.ok check before returning text; throws on HTTP errors (403 path traversal, 404, etc.) instead of rendering error body as source code - SearchBar.tsx: add cleanup useEffect to cancel debounce timer on unmount, preventing setState after unmount - NodeDetailModal.tsx: add cancelled flag with cleanup return to prevent race condition when nodeId changes before in-flight fetch completes MEDIUM: - SwaggerView.tsx iframe: add sandbox="allow-scripts allow-same-origin allow-forms" for defence-in-depth - Dashboard.tsx: hoist O(n²) Object.values().reduce() totals outside .map() for Architecture Layers and Authentication sections Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Remove analyze_codebase tool from mcp-tools.ts (violated read-only contract) - Remove AnalyzeResponse interface from types/api.ts (unused after above) - Remove vendor-d3 manual chunk from vite.config.ts (d3 not imported) - Clean stale build artifacts (39 files from prior builds) - Rebuild frontend with clean output (5 chunks, no d3 dead weight) Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Wrap CodeGraphView in React.lazy() + Suspense so G6 is only loaded when the user navigates to the /graph tab - Remove vendor-g6 from Vite manualChunks so Rollup treats it as a true dynamic dependency (no modulepreload hint in index.html) - Remove d3 / @types/d3 from package.json — never imported anywhere - Rebuild frontend: eager load drops from ~1,880 KB to ~195 KB gzip (vendor-react + main bundle only), well under the 500 KB target Closes RAN-87 Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Escape HTML in G6 tooltip getContent to prevent XSS from filenames - Set graphRef.current immediately after new Graph() and add layout effect cleanup to prevent G6 leaks on rapid layout changes - Replace useApi spread deps [...deps] with JSON.stringify(deps) to fix Rules of Hooks violation - Add combobox/listbox/option ARIA roles and aria-expanded to SearchBar - Remove duplicate ThemeToggle from sidebar; delete dead ThemeToggle.tsx - Surface errors in handleFindCallers/handleFindDeps instead of swallowing - Refactor NodeDetailPanel load into useEffect with isCancelled flag to prevent stale state on rapid navigation Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Cap depth param at config.getMaxDepth() (default 10) in GraphController - Add LIMIT to file-path Cypher in GraphStore with truncated flag (maxFiles, default 10000) - Add @Cacheable(value="file-tree", key="#maxDepth") to QueryService.getFileTree - Remove dead @PostMapping import from GraphController - Add getFileTreeShouldCapDepthAtMaxDepth controller test; update stubs for new signature Closes RAN-124 Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add FileTreeResponse type to types/api.ts matching actual backend shape
{ tree: FileTreeNode[], total_files, truncated }
- Update api.ts getFileTree() to return FileTreeResponse instead of FileTreeNode
- Fix ProjectFileTree.tsx to extract root from treeResponse.tree[0],
resolving undefined nodeCount crash on lines 303/376/379
- Add ErrorBoundary class component in main.tsx wrapping App with
a fallback UI and reload button
Fixes RAN-127 / parent RAN-125.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- api.ts: change getFileTree() return type from FileTreeNode to FileTreeResponse - ProjectFileTree: build synthetic root node from FileTreeResponse.tree array, display total_files count, show truncated warning when response is truncated - ErrorBoundary.tsx: new class component with componentDidCatch logging and reload button fallback UI - main.tsx: wrap App with ErrorBoundary from dedicated component file Fixes RAN-126 / RAN-127 — resolves blank page crash on startup. Co-Authored-By: Paperclip <noreply@paperclip.ing>
- patchIndexHtml() intercepts SPA document requests and serves the on-disk index.html, bypassing the stale JAR-embedded version that loads a broken bundle (index-D3EidrOu.js) causing React to crash. - gotoRoute() timeout raised from default to 30s; uses patchIndexHtml before every navigation. - playwright.config.ts: set tsconfig to tsconfig.test.json; add triple-slash node type reference. - tests/utils/test-helpers.ts: add node:fs/node:path imports + /// <reference types="node" /> directive. Workaround for RAN-128 (stale JAR — mvn rebuild required for clean fix). Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add data-testid="graph-container" (already present), "graph-controls", "graph-minimap", and "breadcrumb" to unlock all E2E graph selectors - Register afterrender listener on G6 graph to set data-render-state="ready" and window.__graphRenderMs after render completes - Reset data-render-state at the start of each renderG6 call to avoid stale state on re-renders Fixes RAN-131 — unblocks graph.spec.ts (15 tests) and performance.spec.ts (7 tests) Co-Authored-By: Paperclip <noreply@paperclip.ing>
…le fix
SearchBar.tsx:
- Change role from combobox to searchbox (tests use getByRole('searchbox'))
- Show dropdown immediately on >=2 chars with loading spinner, not after API response
- Add keyboard navigation: ArrowDown/ArrowUp cycle results, Enter selects, Escape closes
- Use r.name ?? r.label to render result name (matches mock and real API shape)
- Add data-testid=search-dropdown and data-testid=search-spinner for test selectors
- Navigate to /explorer on result selection (not /explorer/:kind which doesn't exist)
types/api.ts:
- Make SearchResult.label optional, add name and filePath fields
tests/utils/test-helpers.ts:
- patchIndexHtml: also serve fresh-built /assets/* from disk, not from stale JAR
This fixes the stale-bundle mismatch where JAR has old JS (role=combobox) but
disk build has new JS (role=searchbox), causing all 11 search tests to hang
Fixes RAN-132
Co-Authored-By: Paperclip <noreply@paperclip.ing>
…dex await - Add @nestjs/ import guard to NestJSControllerDetector and NestJSGuardsDetector to prevent false positives on Angular controllers and generic TypeScript files - Fix EXPOSES edge setTarget() in NestJSControllerDetector (was silently dropped because GraphBuilder.flush() requires edge.getTarget() != null) - Add GuardLinker component: links GUARD/MIDDLEWARE nodes to ENDPOINT nodes via PROTECTS edges using file-path proximity heuristic (same file = match) - Add awaitIndexes(300) after secondary index creation in EnrichCommand to prevent first-request full scans on fresh graphs - Add NestJS false-positive tests and GuardLinker unit tests (9 tests) All 58 relevant tests pass. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…v, ProjectFileTree crash
- SearchBar: separate spinner from dropdown so `[data-testid="search-dropdown"]`
only renders after results are loaded (not while loading). This ensures
ArrowDown/Enter keyboard nav fires with results.length > 0.
Spinner rendered independently when open && loading.
- ProjectFileTree: guard total_files with ?? 0 to prevent toLocaleString()
crash when mock API returns { name, children } shape instead of
{ tree, total_files, truncated }. Fixes gotoRoute timeout in file-tree
search integration test.
- All 11 search.spec.ts tests now pass on chromium.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
allDetected.contains(tag) was skipping edges to any same-file component, not just self-renders. Fix: guard with !tag.equals(comp.name()) so only self-loop edges are excluded while sibling RENDERS edges are preserved. Adds siblingComponentRendersEdgePreserved and selfRenderEdgeNotEmitted tests. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ore modifiers Fixes KafkaDetector silently ignoring Kotlin singleton object declarations (e.g. `object OrderConsumer`) that commonly host @KafkaListener handlers. Also covers internal/open/abstract/sealed/data class modifiers. Adds two new tests: Kotlin object declaration with @KafkaListener, and internal open class with modifiers. Co-Authored-By: Paperclip <noreply@paperclip.ing>
…ibility buildTreeOutput() was constructing tree node maps without a `path` field, breaking the frontend FileTreeNode which requires it for navigation and selection. Path is now accumulated recursively from root to each node. Closes RAN-139 Co-Authored-By: Paperclip <noreply@paperclip.ing>
|
1. ConfigDefDetector: move seenKeys to file scope to prevent duplicate config nodes when multiple classes in the same file reference the same @value or ConfigDef key. 2. ConfigDefDetector: add receiver discriminator to .define() call matching — only flags calls where the scope references "ConfigDef" or "CONFIG" to prevent false positives from unrelated .define() calls. 3. KafkaDetector: extend CLASS_RE to include enum|inline|value|protected Kotlin modifiers (enum class, inline class, value class, protected class). 4. TopicLinker: extend to handle PUBLISHES/LISTENS edge kinds and TOPIC/QUEUE/EVENT/MESSAGE_QUEUE node kinds for full messaging coverage. 5. TopicLinker: add determinism test covering multi-sender/multi-receiver SENDS_TO/RECEIVES_FROM scenarios. These fixes were reviewed and approved in the detection-quality-fixes branch (46fd2b0) but were absent from the phase5 branch. Co-Authored-By: Paperclip <noreply@paperclip.ing>


Summary
get_ego_graph,find_callers,find_dependencies,service_dependents,find_path,list_flows,find_dependents,find_node,find_related_endpoints,run_cypher,list_kindsaccordion,input,dialog,tabs,scroll-areamcp-tools.ts: single source of truth for tool definitions, imported by both pagesWhat changed
src/main/frontend/src/lib/mcp-tools.tssrc/main/frontend/src/components/ui/accordion.tsxsrc/main/frontend/src/components/ui/input.tsxsrc/main/frontend/src/components/ui/dialog.tsxsrc/main/frontend/src/components/ui/tabs.tsxsrc/main/frontend/src/components/ui/scroll-area.tsxsrc/main/frontend/src/components/McpConsole.tsxsrc/main/frontend/src/components/SwaggerView.tsxsrc/main/frontend/package.json@radix-ui/react-accordion,@radix-ui/react-tabssrc/main/frontend/tailwind.config.tsAcceptance criteria
data-testid="param-error")surface-*/brand-*tokens)data-testidattributes matching existing e2e test expectationsTest plan
/console— accordion renders 7 categories, Stats expands by default/api-docs— Swagger UI tab loads iframe, MCP Tools Reference tab shows all 36 toolsnpm run test:e2eagainst a running server🤖 Generated with Claude Code