feat: hub score visualization, HTML export, and Louvain community detection#118
Conversation
File nodes now display border thickness proportional to their import count (hub score 0-100, normalized over visible nodes). Four tiers: 2px (<20), 3px (>=20), 4px (>=50), 5px (>=80). High-score nodes also render bold (>=50) and larger font (>=80, non-root only). Priority order preserved: selection > cycle > root > hub score. External dashed borders coexist with hub tiers. React.memo comparator updated to include hubScore to ensure re-renders on score changes. Coverage: FileNode.tsx 97%, buildGraph.ts 96%, 251 tests passing.
Introduces GraphNodeMetadata {hubScore, loc?, fileExtension?} as an
optional Record on GraphData, keyed by normalizePath. NodeMetadataBuilder
computes hubScore [0-1] from parentCounts in the analyzer layer, making
Spider the single source of truth per ADR-F2-01.
WikiGenerator reads externalNodeMetadata.hubScore when available, falling
back to its DB-computed value. GraphViewService populates nodeMetadata
after parentCounts. buildGraph.ts reads nodeMetadata with a fallback to
computeHubScores for backward compat when nodeMetadata is absent.
MCP types.ts adds GraphNodeMetadataSchema and GraphDataSchema Zod schemas.
477 tests passing, coverage: NodeMetadataBuilder 100%, buildGraph 96%,
mcp/types.ts 86%.
Adds `graph-it export --format html [--output path.html]` that produces a self-contained HTML file with vis-network 10.1.0 inlined (~628KB). - HtmlExporter: htmlEscape on all injected values (Règle 10), safeVisSource guard against </script> sequences, hub score colour tiers - ExportHtmlCommand: parseArgs for --output, normalizePath on all keys - formatter.ts: "html" added to CliOutputFormat + CliError guard - .vscodeignore: exclude CLAUDE.md and .claude/** from .vsix - ADR-F3-01/02: vis.js inlining strategy and dependency position
Detects file clusters via a greedy Louvain phase-1 algorithm (pure Node.js, no external dependency). communityId 0 = isolated, 1+ = cluster index. - LouvainDetector: deterministic phase-1, 100-pass cap, contiguous 1-indexed - communityPalette: 12-color WCAG AA palette in shared/ - communityColor: webview-only util, transparent for isolated/absent nodes - CommunityLegend: overlay bottom-left, label = highest-hubScore node per cluster - FileNode: background tinted by community (13% alpha, Règle 04 compliant) - HtmlExporter: communityId → vis.js node color (palette priority, hubScore fallback) - ExportHtmlCommand: calls computeNodeMetadata to populate communityId for export - GraphProvider test: relaxed to objectContaining for nodeMetadata additions - ADR-F4-01: documents Louvain-native vs graphology decision
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
…export routing
- CommunityLegend: clearer header ("Import clusters" + subtitle), tooltip
updated to "Cluster N — N clusters total"
- HtmlExporter: buildCommunityLegend() injects a fixed bottom-left overlay
with cluster colors and representative node labels into exported HTML
- REPL: /export now only routes to HTML export when --format html is explicit,
preserving existing post-result export (JSON/Markdown/Mermaid)
- Remove stale eslint-disable directive in HtmlExporter.ts
…cs/Reset controls
… shows directory path
…xtension Spider.workspaceRoot getter → GraphViewService passes it to computeNodeMetadata, fixing the "all files same cluster" regression in the extension webview. NodeMetadataBuilder test now passes explicit workspaceRoot='/project'.
Replaces workspacePrefixLen (broken when short files reduce minLen/cap) with exact relative path from workspaceRoot — same logic as PathCommunityDetector. Falls back to workspacePrefixLen when workspaceRoot absent (tests unchanged).
When state.workspaceRoot differs from runtime.workspaceRoot (user ran /path ./src/), use it as the default scope for /export. Falls back to state.lastFile if set.
ExportHtmlCommand wraps path.resolve(outputPath) with normalizePath → forward slashes on Windows. Test assertions use normalizePath consistently.
…port changelog.md: v1.11.0 entry (PathCommunityDetector, HTML vis.js export, /export REPL, bug fixes) docs/CLI.md: add /export to REPL slash commands table README.md: add /export to REPL list + graph-it export to CLI table
computeNodeMetadata called after spider.crawl — same path-based community detection as VS Code extension and CLI. NodeInfo gains hubScore + communityId fields.
- buildNodeInfo: 4 nouveaux cas — metadata undefined (pas de hubScore/communityId), metadata peuplée (hubScore + communityId présents), clés partielles, lookup normalisé (Règle 03) - executeCrawlDependencyGraph: 2 nouveaux cas — hubScore présent sur chaque nœud, communityId distinct entre nœuds dans des répertoires différents
…munity fields - graph.ts: pass graphData.nodes (normalized) to buildNodeInfo so metadata keys match nodeMetadata index on Windows (Règle 03, Sophie BLOCK fix) - changelog.md + docs/CLI.md: document hubScore/communityId on crawl_dependency_graph nodes
…r/src/, etc.) PathCommunityDetector now strips the common dir prefix shared by ALL input files before applying UMBRELLA skip. This prevents projects where all sources live under a non-umbrella container dir (e.g. 'vue/') from collapsing to a single cluster. Algorithm: path.relative(workspaceRoot) → strip common prefix (capped at minLen-1) → skip UMBRELLA dir → domain = first remaining dir segment. ReactFlowGraph.tsx communityLabel uses the same two-stage stripping (absolute prefix then relative common prefix) for correct legend labels. Fixes: all-same-cluster bug when workspace root is the monorepo root (app-bobbee case).
Divergent top-level roots (vue/src/ + backend/) collapsed to one cluster each because common-prefix stripping is empty when roots diverge, and container dirs like 'vue' are not umbrella dirs. New rule: skip up to and INCLUDING the last umbrella dir (src, tests, lib...), domain = next segment. Handles vue/src/views -> 'views', packages/app/src/store -> 'store'. Applied in PathCommunityDetector (analyzer) and ReactFlowGraph legend (webview) with matching logic. Adds domainFromDirParts export + 3 tests (divergent roots, deeply nested).
/export executed correctly but was missing from SLASH_COMMANDS, so it never appeared in the Tab-completion suggestion list.
New VS Code setting graph-it-live.showCommunities (boolean, default true). When false, hides the community legend and node background tint on the file graph. Applies live via onDidChangeConfiguration (no reload). Wired end-to-end: config -> ProviderStateManager -> GraphProvider (_sendGraphUpdate initial + enriched messages) -> ShowGraphMessage -> App.tsx state -> ReactFlowGraph prop -> buildGraph -> FileNodeData tint gate.
Property tests write temp_*.py into the committed fixtures dir and unlink in a per-test finally, which is not crash-safe: a killed run orphans the file (observed a 0-byte temp_mixed_types_*.py). Add beforeAll/afterAll sweep (fs.readdir + unlink, cross-OS, no shell) and an anchored .gitignore guard so orphans never get committed.
Summary
GraphNodeMetadatafoundation:hubScore,fileExtension,loccomputed byNodeMetadataBuilder(Spider as single source of truth)graph-it export --format html+/exportin REPL: standalone HTML with vis.js 10.1.0 inlined, community colors,htmlEscapeon all injected values (Règle 10)communityIdinGraphNodeMetadata, palette 12 colors WCAG AA, legend in ReactFlow (representative node label), community colors in HTML exportKey decisions
NodeMetadataBuilderis the single enrichment point (hubScore + communityId), called byGraphViewServiceandExportHtmlCommandCOMMUNITY_PALETTEinshared/,communityColor()webview-only (Règle 05)communityId: 0= isolated node,1+= cluster, absent = not yet computed10.1.0independencies(runtimerequire.resolve, not bundled — Règle 02 safe).vscodeignore:CLAUDE.mdand.claude/**excluded from .vsixTest plan
npm test— 2179 pass, 12 skippednpm run check:types— cleannpm run package:verify— zero .map files, .vsix 19.5 MBnode bin/graph-it export --format html --output graph.html— open in browser, verify community colors + legendgraph-it→/export --output graph.html