diff --git a/README.md b/README.md index e4bd7fb9..32270c74 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,40 @@ Embedded storage is selected automatically on Linux x64. On Apple silicon macOS, Set `CODEGRAPH_RAW_TOOLS=1` to expose the lower-level handlers instead of the four grouped tools. +## Dashboard + +CodeGraph ships a browser dashboard for exploring the graph visually: a force, +tree, or ring view of files, functions, classes, and interfaces, semantic and +Cypher search, a source viewer with syntax highlighting, and an operations tab +for indexing and embedding coverage. + +It runs as a second binary that serves both the UI and the REST API on one +port, so nothing else needs to be started. + +```bash +# published package: the binary ships inside codegraph-mcp, so name the +# package explicitly. A bare "npx codegraph-dashboard" would look for a +# package of that name, which does not exist. +npx -p codegraph-mcp codegraph-dashboard + +# already installed globally +npm install --global codegraph-mcp && codegraph-dashboard + +# source checkout +pnpm dashboard +``` + +Then open . Set `API_PORT` to use a different port. + +The dashboard is optional. The MCP server does not start it, and running the +MCP server does not require it. + +| Variable | Purpose | +| --- | --- | +| `API_PORT` | Port for the dashboard and REST API (default 3001) | +| `CODEGRAPH_DASHBOARD_DIR` | Override the location of the built dashboard assets | +| `CODEGRAPH_CORS_ORIGINS` | Comma separated origin allowlist, for a shared deployment | + ## Configuration | Variable | Purpose | @@ -166,8 +200,10 @@ FalkorDBLite's Linux x64 and Apple silicon macOS binaries are installed with the | [`@codegraph/cli`](packages/cli/) | Source-checkout command-line tools | | [`codegraph-mcp`](packages/npm-package/) | Public npm distribution staging and entry point | | [`@codegraph/mcpb`](packages/mcpb/) | Platform-local MCPB desktop extension build | +| [`@codegraph/api`](packages/api/) | REST API consumed by the dashboard | +| [`@codegraph/dashboard`](packages/dashboard/) | Static dashboard UI served by the API | -The Next.js application lives in [`apps/web`](apps/web/), and the reproducible search benchmark lives in [`benchmarks/cgbench-v1`](benchmarks/cgbench-v1/). +The marketing site lives in [`apps/web`](apps/web/), and the reproducible search benchmark lives in [`benchmarks/cgbench-v1`](benchmarks/cgbench-v1/). ## License diff --git a/packages/dashboard/src/components/dashboard/app-shell.tsx b/packages/dashboard/src/components/dashboard/app-shell.tsx index ed57f734..4eea8fb7 100644 --- a/packages/dashboard/src/components/dashboard/app-shell.tsx +++ b/packages/dashboard/src/components/dashboard/app-shell.tsx @@ -48,7 +48,17 @@ export function AppShell({ projectId }: { projectId?: string | null }) { setHighlightedNames(new Set([name]))} + onSelectResult={(result) => { + setHighlightedNames(new Set([result.name])) + // Open the detail panel too. The search payload already carries + // filePath and line numbers, which is everything the panel needs. + setSelectedNode({ + id: `${result.nodeType}:${result.filePath ?? ''}:${result.name}`, + label: result.name, + type: result.nodeType, + properties: result, + }) + }} /> diff --git a/packages/dashboard/src/components/dashboard/graph-canvas.tsx b/packages/dashboard/src/components/dashboard/graph-canvas.tsx index a486897a..a94451b4 100644 --- a/packages/dashboard/src/components/dashboard/graph-canvas.tsx +++ b/packages/dashboard/src/components/dashboard/graph-canvas.tsx @@ -52,11 +52,16 @@ export function GraphCanvas({ apiUrl, onNodeSelect, highlightedNames, hiddenEdge const nodeType = (n.label ?? nodeData.type ?? 'Unknown') as string return { data: { + // Spread first: the graph payload carries its own internal "id", + // and spreading it last replaced the cytoscape node id. Every edge + // referencing the real id was then treated as an orphan and + // dropped, so functions, classes and interfaces rendered with no + // edges at all while files, which carry no inner id, looked fine. + ...nodeData, id: n.id as string, label: displayName, type: nodeType, filePath: n.filePath as string | undefined, - ...nodeData, }, } }) diff --git a/packages/dashboard/src/components/dashboard/search-panel.tsx b/packages/dashboard/src/components/dashboard/search-panel.tsx index 50e06d42..98974897 100644 --- a/packages/dashboard/src/components/dashboard/search-panel.tsx +++ b/packages/dashboard/src/components/dashboard/search-panel.tsx @@ -2,18 +2,21 @@ import { useState, useCallback } from 'react' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' -interface SearchResult { +export interface SearchResult { name: string nodeType: string filePath?: string callerCount?: number importerCount?: number + // The search endpoint returns the full node payload (startLine, docstring, + // params and so on). Those extra fields are what the detail panel renders. + [key: string]: unknown } interface SearchPanelProps { apiUrl: string onHighlight: (names: string[]) => void - onSelectResult: (name: string) => void + onSelectResult: (result: SearchResult) => void } export function SearchPanel({ apiUrl, onHighlight, onSelectResult }: SearchPanelProps) { @@ -82,7 +85,7 @@ export function SearchPanel({ apiUrl, onHighlight, onSelectResult }: SearchPanel