Skip to content

feat(web): Introduce Tree View and Circles View in Web Viewer#1799

Merged
magyargergo merged 11 commits into
abhigyanpatwari:mainfrom
hugogu:feature/viewer-update
May 26, 2026
Merged

feat(web): Introduce Tree View and Circles View in Web Viewer#1799
magyargergo merged 11 commits into
abhigyanpatwari:mainfrom
hugogu:feature/viewer-update

Conversation

@hugogu

@hugogu hugogu commented May 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Add alternate graph layouts to the web viewer with new graph view state, canvas controls, adapters, and Sigma layout logic for tree and concentric-circle rendering. Include layout and adapter tests plus tree-view E2E coverage, and tune node visibility, edge layering, and large-graph behavior so the new views stay readable.

The views are switchable via a set of toggle buttons:
image

The original view :

image

The circles view for the same data:

image

Another original view:

image

Equivalent circles view:
image

Equivalent Tree View:
image

Motivation / context

Close #1797

Areas touched

  • gitnexus/ (CLI / core / MCP server)
  • gitnexus-web/ (Vite / React UI)
  • .github/ (workflows, actions)
  • eval/ or other tooling
  • Docs / agent config only (AGENTS.md, CLAUDE.md, .cursor/, llms.txt, etc.)

Scope & constraints

In scope

  • Add a new view of diagram

Explicitly out of scope / not done here

N/A

Implementation notes

There are two extra views are introduced.

Testing & verification

  • cd gitnexus && npm test
  • cd gitnexus && npm run test:integration (if core/indexing/MCP paths changed)
  • cd gitnexus && npx tsc --noEmit
  • cd gitnexus-web && npm test (if web changed)
  • cd gitnexus-web && npx tsc -b --noEmit (if web changed)
  • Manual / Playwright E2E (note environment — see gitnexus-web/e2e/)

Risk & rollout

Checklist

  • PR body meets repo minimum length (workflow may label short descriptions)
  • If AGENTS.md / overlays changed: headers, scope block, and changelog updated per project conventions
  • No secrets, tokens, or machine-specific paths committed

@vercel

vercel Bot commented May 24, 2026

Copy link
Copy Markdown

@hugogu is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

Comment thread gitnexus-web/src/hooks/useSigma.ts Fixed
@hugogu hugogu force-pushed the feature/viewer-update branch from 137a3ed to 72aa3d8 Compare May 24, 2026 10:27
@hugogu hugogu changed the title [WIP] Introduce Tree View and Circles View in Web Viewer Introduce Tree View and Circles View in Web Viewer May 24, 2026
@hugogu hugogu changed the title Introduce Tree View and Circles View in Web Viewer feat(web): Introduce Tree View and Circles View in Web Viewer May 24, 2026
@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Tests success unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
9938 9934 0 4 627s

✅ All 9934 tests passed

4 test(s) skipped — expand for details
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 79.71% 34508/43289 N/A% 🟢 ███████████████░░░░░
Branches 68.22% 22016/32270 N/A% 🟢 █████████████░░░░░░░
Functions 84.76% 3572/4214 N/A% 🟢 ████████████████░░░░
Lines 83.2% 31095/37373 N/A% 🟢 ████████████████░░░░

📋 View full run · Generated by CI

@hugogu hugogu force-pushed the feature/viewer-update branch from 72aa3d8 to 37f3899 Compare May 24, 2026 11:12
@github-actions

github-actions Bot commented May 24, 2026

Copy link
Copy Markdown
Contributor

✨ PR Autofix

Found fixable formatting / unused-import issues across 22 changed lines. Comment /autofix on this PR to apply them, or run npm run lint:fix && npm run format locally.

{"schema":"gitnexus.pr-autofix/v2","state":"fixes-available","pr_number":1799,"changed_lines":22,"head_sha":"574d6c65bcba73a64bddfd08c45f0e128a7e2873","run_id":"26361611358","apply_command":"/autofix"}

@hugogu hugogu force-pushed the feature/viewer-update branch 3 times, most recently from 574d6c6 to 94a00db Compare May 24, 2026 13:16
@github-actions

github-actions Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 7m 47s —— View job


Production-Readiness Review: PR #1799 — Tree View and Circles View in Web Viewer

Tasks

  • Post initial comment
  • Read required repo docs (DoD.md, AGENTS.md, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md)
  • Verify current git state (HEAD SHA, commits, changed files)
  • Inspect all 18 changed files
  • Inspect related unchanged files (graph loading, streaming, existing Sigma/Force setup)
  • Run hidden Unicode/hygiene checks
  • Assess all 6 review lanes (large-graph rendering, React lifecycle, layout correctness, UX/a11y/i18n, tests/CI, branch hygiene)
  • Synthesize findings and post full review

Review bar for this PR

Applied: DoD.md v2.0.0, AGENTS.md v1.7.0, GUARDRAILS.md, CONTRIBUTING.md, TESTING.md, ARCHITECTURE.md (read; web-only, no pipeline boundaries in scope). All five DoD review gates applied (correctness, readability, architecture, security, performance). The PR-specific bar — large-graph readability improvement without browser freezes, React/Sigma lifecycle safety, accessible controls, complete i18n, valid CI — was used as the primary acceptance criteria.

DoD.md — missing docs: All required docs present.


Problem being solved

Issue #1797 (open): the force-directed graph becomes an unreadable dense cluster for moderately large repositories. Acceptance criteria include meaningfully easier readability for large graphs, distinguishable communities, structural navigation, preserved force graph behavior, and reasonable browser performance. This PR adds Tree View and Circles View as switchable alternatives, not replacements.


Current PR state

HEAD SHA: 1519e0e5f450dde03b2ca84924b84c06e328293b (a merge-from-main commit)

Commit list (3 commits from base):

  • 94a00db — feat(graph-view): add tree and circles layout modes (the feature)
  • 67f4f65 — Merge branch 'main' into feature/viewer-update (first sync)
  • 1519e0e — Merge branch 'main' into feature/viewer-update (second sync, current HEAD)

The head SHA visible in the PR (94a00db) is NOT the current head; 1519e0e is. The visible PR diff is accurate after accounting for the merge commits.

CI status: All checks passed per the bot comment (typecheck ✅, unit tests ✅ across 3 platforms, E2E ✅ for web changes). Vercel deployment is blocked pending team authorization. PR Autofix found 22 fixable lines; whether the current head resolved them is unknown (no second autofix comment). CodeQL flagged an unused variable in useSigma.ts at line 1314 — unresolved.


Merge status and mergeability

Status: checks pending / review blocked

All automated CI checks passed for the current head. However the Vercel authorization is unresolved, the CodeQL security warning is unresolved, and the PR Autofix comment is unresolved. Branch protection requiring these is unknown, but all three must be addressed. Based on visible state: checks passing, but bot comments unresolved — not currently mergeable under standard production-readiness criteria.


Repository history considered

Prior work Lesson applied
PR #362 (closed, LOD rendering) This PR is narrow and web-only — passes the narrowness bar. Does not reintroduce LOD or server-side changes.
PR #732 (merged, NDJSON streaming) Streaming path is in the HTTP client layer. This PR only processes the already-parsed KnowledgeGraph object and does not touch backend-client.ts, streaming, fallback, or timeout logic. PR #732 is not regressed.
Issue #495 (invalid string length, 479K nodes) No server changes. Web-side impact: synchronous tree layout at this scale would be catastrophic (see Finding A).
Issue #705 (70MB /api/graph, 200s load, 58K nodes) Open. This PR does not help load time; it adds potentially O(N × E) synchronous work after loading completes.
Issue #761 (70K nodes hang on "Downloading graph") Open. Same concern: PR adds more synchronous work after graph load.

Branch hygiene assessment

Classification: merge-from-main present but harmless and merge-safe

Both merge-from-main commits (67f4f65, 1519e0e) bring exclusively non-web backend fixes (PHP scope synthesis, group manifest extraction, Java MIGRATED_LANGUAGES, CI parity shards, Windows windowsHide). No web files were touched by these merges. The feature diff at 94a00db is causally connected: every changed file serves Tree View or Circles View. No generated artifacts, no workflow churn, no lockfile churn.


Understanding of the change

The PR introduces graphViewMode: 'force' | 'tree' | 'circles' into GraphStateContext. Switching modes converts the raw KnowledgeGraph into a new graphology Graph (via the appropriate adapter), hands it to sigma.setGraph(), and starts a custom rAF physics loop for tree/circles or ForceAtlas2 for force.

  • Tree View: calculateTreeLayout computes static initial positions (type-layered grid + proportional X allocation + 6-pass parent centering + 14-iteration spring relaxation), all synchronously. runTreeLayout then refines these positions via a rAF loop.
  • Circles View: calculateCirclesLayout computes initial ring positions via parent-centred arc allocation (O(N+E), no spring iteration). runCirclesLayout refines via rAF with adaptive large-graph scaling (repulsion/spread disabled at >5K nodes).
  • Mode switching: handleViewModeChange clears selection + hovered state + resets zoom, then graphViewMode state change triggers useEffect to rebuild the sigma graph.
  • Lifecycle cleanup: stopAllLayouts cancels FA2 worker and tree/circles rAF frames before starting a new layout. Sigma init/cleanup is in a single-mount useEffect with proper cleanup.

Findings


Finding A: Synchronous tree layout freezes the main thread for large graphs

Severity: blocker
Status: confirmed

Risk: calculateTreeLayout runs entirely on the main thread, blocking UI interaction and rendering for the duration. The spring-relaxation phase (14 iterations × all edges + enforceLayerSpacing O(N log N) per layer) runs synchronously inside knowledgeGraphToTreeGraphology, which is called from a useEffect inside GraphCanvas with no worker, chunk, cap, or guard.

Evidence found:

  • tree-layout.ts:504-548: 14 spring iterations, each scanning all graph.relationships (O(E)) plus all nodes (O(N)) for force accumulation.
  • tree-layout.ts:545-547: enforceLayerSpacing called per layer per spring iteration — O(N log N) per layer per iteration.
  • tree-layout.ts:552-563: Final centering pass over all nodes.
  • GraphCanvas.tsx:200-201: knowledgeGraphToTreeGraphology(graph) called synchronously in a useEffect with no size guard.

Algorithmic bounds (confirmed, not estimated):
At 70K nodes / 200K edges (Issue #761 scale): 14 × (200K + 70K) + 14 × O(70K log 70K) ≈ 14 × 270K + 14 × 1.1M ≈ 19M operations. At 338K nodes / 1.4M edges (PR #732 validated scale): 14 × 1.7M + 14 × ~5M ≈ 94M operations. In real JavaScript with Map-based force accumulation (Map.get/set at each of the 1.4M edge accesses × 14 = 20M Map calls), the main thread will be blocked for multi-second to multi-minute durations. The circles initial layout is O(N+E) only and does not have this problem.

Recommended fix: Gate tree view (and large circles view) behind a node-count check. For N > a documented threshold (e.g. 10,000 nodes), either: (a) show a "graph too large for tree view" message; (b) chunk the layout with queueMicrotask / chunked requestIdleCallback; or (c) move calculateTreeLayout to a Web Worker. A simple and safe short-term fix is a count guard with a user-visible fallback message. The rAF physics in runTreeLayout is already well-structured for incremental execution.

Blocks merge: YES


Finding B: E2E tests skip when services absent; coverage is shallow

Severity: high
Status: confirmed

Risk: tree-view.spec.ts uses test.skip() when localhost:4747 or localhost:5173 are unavailable. The CI report says "E2E: success" but the E2E job for web changes likely runs against a real server. If services go down or CI changes, the tests silently pass without validating the feature.

Evidence found:

  • e2e/tree-view.spec.ts:17-48: test.beforeAll checks both servers; calls test.skip(true, ...) on any failure.
  • The tests themselves only verify: (1) tab class changes, (2) a sidebar span click produces a "Clear" button. Neither test verifies that the sigma canvas rendered the correct layout, that physics ran, that edges are present, or that mode switch rebuilds the graph correctly.
  • graph.test.tsx: Only 2 tests — default mode and one setter test. No test covers mode switching under filter changes, selection interactions, or large graphs.
  • No synthetic performance test exists for the layout algorithms at realistic graph sizes.

Recommended fix: Add non-skipping vitest integration tests covering mode-switch behavior with a fixture graph (these can run without the backend). Add at least one test verifying that switching to tree mode while a node is selected resets selection. Add a bounded algorithmic test asserting that calculateTreeLayout on N=5000 nodes + E=10000 edges completes within an acceptable wall-clock budget.

Blocks merge: YES (together with Finding A, the production feature is effectively untested for its core value proposition of large-graph readability)


Finding C: Mode switch clears all user selection state without warning

Severity: medium
Status: confirmed

Risk: Switching view modes destroys the selected node, hover state, and resets zoom. A user who selects a node, switches to tree view, and then back to force view has lost their selection. This is counter to typical tab/view switching UX where semantic state is preserved.

Evidence found:

  • GraphCanvas.tsx:163-173: setSelectedNode(null), setSigmaSelectedNode(null), setHoveredNodeName(null), resetZoom() all called unconditionally on mode change.
  • graph.test.tsx: No test for mode-switch-with-selection behavior.

Recommended fix: Preserve appSelectedNode across mode switches. The selected node ID is stable across graph rebuilds. Only reset layout-specific sigma state (sigma's internal selected ref) and re-apply the selection after the new graph is set. Or at minimum document this as intentional behavior.

Blocks merge: no (UX regression, not a correctness or safety issue)


Finding D: Force graph behavior — no regression detected

Status: confirmed (no regression)

knowledgeGraphToGraphology is unchanged. The useEffect dependency array now includes graphViewMode, which is correct — the filter effect must re-run after a mode switch because the sigma graph changes. The FA2 worker lifecycle (runLayout, stopAllLayouts) is unmodified from its pre-PR behavior when in force mode. The force graph path is isolated by the graphViewMode === 'tree' / === 'circles' branches.


Finding E: Math.random() in edge curvature for tree and circles adapters

Severity: medium
Status: confirmed

Risk: knowledgeGraphToTreeGraphology (line 407) and knowledgeGraphToCirclesGraphology (line 487) both use Math.random() for edge curvature. Edges get different curvature values each time the graph is rebuilt (i.e., every mode switch). This causes visible edge jitter when returning to tree or circles view, and makes tests that check edge attributes non-deterministic.

Evidence found:

  • graph-adapter.ts:407: curvature: 0.1 + Math.random() * 0.1 — in tree adapter
  • graph-adapter.ts:487: curvature: 0.1 + Math.random() * 0.1 — in circles adapter
  • The deterministicHash function already exists in both tree-layout.ts and circles-layout.ts and could be reused to generate stable curvature from edge IDs.
  • Note: The force adapter also uses Math.random() for node positions (pre-existing, not introduced by this PR).

Recommended fix: Replace Math.random() with deterministicHash(rel.id + 'curve') * 0.1 + 0.1 in tree and circles adapters. The deterministicHash function is already implemented in both layout files; export it from a shared utility or from graph-adapter.ts.

Blocks merge: no (but causes visible flicker on mode re-entry)


Finding F: Constants duplicated between layout files and useSigma.ts

Severity: medium
Status: confirmed

Risk: CIRCLES_RING_RADII, CIRCLES_BAND_HALF, RING_COUNT are defined independently in circles-layout.ts (exported) and redefined locally in useSigma.ts (lines 156-169). The comments in useSigma.ts explicitly warn "must match CIRCLES_BAND_HALF in circles-layout.ts" — this is an acknowledged drift risk, not a false alarm.

Evidence found:

  • circles-layout.ts:20-30: export const CIRCLES_RING_RADII = [90, 240, 420, 620], export const CIRCLES_BAND_HALF = 45
  • useSigma.ts:156-169: const CIRCLES_RING_RADII = [90, 240, 420, 620] as const, const CIRCLES_BAND_HALF = 45 — no import from circles-layout.ts
  • DoD §2.3: "No copy-paste duplication created for convenience."

Recommended fix: Import and reuse CIRCLES_RING_RADII, CIRCLES_BAND_HALF, and RING_COUNT from circles-layout.ts in useSigma.ts. They are already exported.

Blocks merge: no


Finding G: CodeQL unused variable r at useSigma.ts:1314

Severity: medium
Status: confirmed

Risk: The variable r = Math.sqrt(x*x + y*y) || 1 at useSigma.ts:1314 is computed inside the circles layout force-application loop but never used. The comment at lines 1316-1317 explains that the radial resistance decomposition was removed. r is a leftover.

Evidence found:

  • useSigma.ts:1312-1318: r is assigned from Math.sqrt(...) but the next used variables are rawVx and rawVy which do not reference r.
  • Two other uses of r at lines 1160 and 1283 are legitimately used. Only the one at 1314 is unused.
  • The GitHub Advanced Security AI flagged this comment on the PR (as of the review date) and it is unresolved.

Recommended fix: Remove const r = ... at line 1314 (and lines 1312-1313 if x and y are also unused elsewhere in that block). Confirm with npx tsc --noEmit and re-run CI.

Blocks merge: no (false-positive for runtime risk but the warning must be resolved before merge per the PR DoD)


Finding H: Hidden Unicode hygiene check

Status: unverified (sandbox restriction prevented running Python/grep)

The Python Unicode scanner and grep -P with hex character class patterns required subprocess execution that was blocked in this environment. Manual inspection of all changed TypeScript and JSON files found no suspicious patterns: no unusual whitespace in identifiers, no control characters visible in the code, and locale files contain only legitimate Chinese/ASCII characters. This cannot be marked confirmed clean — the maintainer should run the scanner locally:

git diff --name-only origin/main...HEAD | xargs grep -nP "[\x{202A}-\x{202E}\x{2066}-\x{2069}\x{200B}\x{200C}\x{200D}\x{FEFF}]" --

Blocks merge: no (pending maintainer verification)


PR-specific assessment sections

Browser large-graph rendering

The circles view is safe for graphs up to ~5K nodes (repulsion/spread disabled above that threshold, gravity + spring only at 30s budget). At 70K nodes the circles physics is still O(N+E) per frame which at 30fps = 30 × 270K ≈ 8M ops/sec — feasible in a worker but may cause frame drops in the main thread since runCirclesLayout runs via requestAnimationFrame synchronously.

The tree view is not safe at known large-graph sizes (see Finding A). The synchronous calculateTreeLayout is the blocking issue. The physics refinement (runTreeLayout) is well-designed with proper adaptive stopping, X-sorted repulsion (O(N·k) not O(N²)), and frame-rate awareness — but these benefits only matter if users can survive the initial layout computation.

The force graph path is unmodified and continues to use the FA2 worker + noverlap combination from before this PR.

React state and lifecycle

  • graphViewMode lives in GraphStateContext with a single owner. The useMemo wrapping the context value correctly includes graphViewMode in its dependency array.
  • The useEffect dependency arrays in GraphCanvas.tsx are correct: the graph-build effect depends on [graph, nodeById, setSigmaGraph, graphViewMode]; the filter effect depends on [graph, graphViewMode, visibleLabels, depthFilter, appSelectedNode].
  • The filter effect's include of graphViewMode ensures filters re-apply when the sigma graph changes due to mode switch. This is correct.
  • Sigma lifecycle: the cleanup function in the single-mount useEffect correctly cancels both tree and circles rAF frames, clears velocity maps, kills the FA2 layout, and kills sigma.
  • stopAllLayouts correctly stops all three layout types before starting a new one via setGraph.
  • graphRef.current !== graph guard in the tree/circles step functions prevents stale closure issues after graph replacement.
  • One issue: setGraph creates a new callback every time options.layoutMode changes. Since options.layoutMode is graphViewMode, this creates a new setGraph on every mode switch — which is correct — but it also means the useEffect that calls setSigmaGraph re-runs due to both graphViewMode AND setSigmaGraph being in its dependency array (they change together). React 18 batches these and runs the effect once, which is correct.

Layout correctness and graph semantics

  • Both tree and circles layouts use deterministicHash for Y/radial jitter — correctly deterministic for node positions.
  • Edge curvature uses Math.random()non-deterministic (see Finding E).
  • The TYPE_TO_LAYER / TYPE_TO_RING maps are duplicated between the two layout files but with identical content — a cleanliness concern but not a semantic error.
  • Layer semantics are inverted visually (containers at bottom/innermost, functions at top/outermost), which the tests correctly verify.
  • Tree layout preserves relationship direction via the two-pass edge insertion (hierarchy first, cross-cutting second). Circles layout does the same.
  • knowledgeGraphToCirclesGraphology correctly does not set treeAnchorX/Y (and vice versa), preventing stale attributes from leaking across modes.

UX/accessibility/i18n

  • View mode buttons at GraphCanvas.tsx:276-309 have visible text labels (not icon-only) — good.
  • Missing type="button" attribute — buttons not inside a form so harmless in practice, but against HTML best practice.
  • Missing aria-pressed: the active state is conveyed only via bg-accent CSS class. Screen readers cannot determine which view is active.
  • No role="tablist"/"tab": semantically these are tab-like controls; grouping them with ARIA tab roles would be cleaner.
  • Keyboard reachability: buttons are standard <button> elements in the DOM flow — keyboard operable.
  • Localization: both en/graph.json and zh-CN/graph.json have complete, consistent translations for canvas.viewModes.force/tree/circles. No hardcoded strings visible. Chinese translations are semantically correct (力导向图 / 树形图 / 同心圆).
  • FileTreePanel.tsx icon changes: added Target, List, AtSign, Type imports — these are used in the icon map for Const/Property/Decorator/Enum/Type nodes. The filter-panel.test.ts verifies the icon-to-label mapping. This is causally connected (the PR adds Const and Property to DEFAULT_VISIBLE_LABELS in constants.ts, which were already needed for Kotlin support). Not unrelated churn.

Tests and CI

Test What it covers Gap
tree-layout.test.ts 8 tests for layer ordering, grid distribution, parent-child X alignment, edge attraction No performance bound for large N
graph-adapter.test.ts 3 tests for tree adapter: node presence, hierarchy vs cross-cutting edge styling, IMPORTS as cross-cut No circles adapter tests; no determinism test; no large-N test
graph.test.tsx Default mode = 'force', setter works Only 2 tests; no mode-switch-with-selection or filter interaction
tree-view.spec.ts Tab switching active state, file tree click → "Clear" button appears Skips without services; no graph render verification
filter-panel.test.ts Icon mappings, color legend coverage Correctly covers the new Property/Const/Target icons

Notable absence: no test for knowledgeGraphToCirclesGraphology, no test that changing graphViewMode correctly routes to the right adapter, no test that the filter effect re-applies on mode switch, and no synthetic performance guard.

Vitest config change (vitest.config.ts): the change updated the mermaid alias path — unrelated to this PR's feature but may be a housekeeping fix.

Branch hygiene and repository history

Two merge-from-main commits present; both bring only non-web backend fixes. All 18 changed files are causally connected to the tree/circles feature. No workflow churn, no lockfile churn, no generated artifacts. The branch is narrowly focused — satisfies the PR #362 precedent of staying focused.

Hidden Unicode and hygiene

  • Conflict markers: none found (visual inspection of all changed files).
  • Console logs/debug flags: none introduced.
  • TODOs without issue references: none.
  • Hardcoded localhost: tree-view.spec.ts:14-15 uses localhost:4747 / localhost:5173 as defaults with process.env overrides — acceptable for E2E setup.
  • Skipped tests: tree-view.spec.ts skip is conditional on service availability — documented behavior.
  • Hidden Unicode: could not run scanner (sandbox restriction); manual inspection found nothing suspicious.
  • Unused variable at useSigma.ts:1314 (const r) — CodeQL confirmed, unresolved.

Back-and-forth avoided by verifying

  1. The merge-from-main commits were verified directly in git log — confirmed harmless, no web changes.
  2. The current HEAD was verified (1519e0e, not 94a00db as visible in some PR UI views).
  3. deterministicHash was confirmed present in both layout files — avoiding a question about whether random jitter was justified.
  4. The FA2/noverlap streaming path was confirmed unmodified — PR [codex] fix large repository graph loading #732 is not regressed.
  5. Constants duplication was directly verified by reading both files — the comment "must match" confirms this is an acknowledged risk, not just a style concern.

Open questions (unavoidable)

  1. What is the supported graph size for Tree View? The PR is motivated by large-graph readability but documents no limit. Is there an intended cap, or is the expectation that calculateTreeLayout is fast enough at the sizes users have (e.g., is the real-world p99 graph size < 5K nodes)?

  2. Was the autofix comment resolved by the latest push? The autofix bot found 22 fixable lines on the prior head. The current head is newer but no second autofix comment appeared.

  3. Is selection-clearing on mode switch intentional product decision? The PR description does not address it.


Final verdict

not production-ready

The implementation of tree and circles layouts is architecturally sound, well-tested at the unit level, and properly localized. However, Finding A (synchronous calculateTreeLayout main-thread blocking) is a confirmed blocker for the PR's stated goal of improving large-graph readability: the feature cannot be safely used for the repository sizes documented in open issues #705 and #761, and will produce multi-second to multi-minute browser freezes at those scales. The PR makes no claim about supported graph sizes and provides no user-facing guard, which means the feature would be harmful rather than helpful for large repositories.

Additionally, the CodeQL warning (unused variable r, Finding G) is unresolved and must be addressed before merge per the project's own review gates. The E2E coverage (Finding B) relies on conditional skipping and does not validate the core graph-switching behavior independently of live services. Together, these gaps mean the feature is not validatable in CI without live services and is actively unsafe for the target use case.

The recommended path to merge: (1) add a node-count guard with a user-visible fallback for tree view at the large-graph threshold; (2) remove the unused r variable to clear the CodeQL warning; (3) replace Math.random() edge curvature with deterministicHash; (4) import shared constants from circles-layout.ts instead of duplicating; (5) add aria-pressed to view mode buttons; (6) add at least one non-skipping vitest test covering mode-switch behavior with a fixture graph.

@magyargergo

Copy link
Copy Markdown
Collaborator

@hugogu could you please review these findings from claude? 🙏

@hugogu

hugogu commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

@magyargergo sure, will try do it today.

@magyargergo

magyargergo commented May 25, 2026

Copy link
Copy Markdown
Collaborator

@magyargergo sure, will try do it today.

I recommend looking up other PRs since they might have addressed this problem for you, they just weren't merged in because the main branch diverged so much that it would have been too much effort to rebase. Plus, we didn't have time to pick this up. So, we really appriciate your work here!

@magyargergo

Copy link
Copy Markdown
Collaborator

Also some layouts that you may find interesting https://cambridge-intelligence.com/blog/automatic-graph-layouts/

@hugogu

hugogu commented May 25, 2026

Copy link
Copy Markdown
Contributor Author

Also some layouts that you may find interesting https://cambridge-intelligence.com/blog/automatic-graph-layouts/

Thanks for sharing it, it's a great reference, I realize the new Circles view is essentially the Radical Layout in it, and the Tree View is essentially the virtical Sequential Layout . Let me rename them in UI.

I'm interested to implement some of the other layouts over time, gradually. (I suppose I shall not raise one PR with 10,000+ lines of code changes, shall I?)

And it is my habit to rebase onto latest main if my PR fall behind, not a trouble to me.

@magyargergo

Copy link
Copy Markdown
Collaborator

Ideally, we should do it incrementally

@hugogu hugogu force-pushed the feature/viewer-update branch from 8cd4667 to 419e831 Compare May 25, 2026 13:07
Comment thread gitnexus-web/src/hooks/useSigma.ts Outdated
Comment thread gitnexus-web/src/lib/graph-adapter.ts Outdated
Comment thread gitnexus-web/src/lib/graph-adapter.test.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Code review

CLAUDE.md compliance — GitNexus workflow rules

Per CLAUDE.md (GitNexus rules section):

MUST run impact analysis before editing any symbol. Before modifying a function, class, or method, run gitnexus_impact({target: "symbolName", direction: "upstream"}) and report the blast radius to the user.

MUST run gitnexus_detect_changes() before committing to verify your changes only affect expected symbols and execution flows.

This PR modifies several indexed symbols (knowledgeGraphToGraphology, GraphStateProvider, AppStateProviderInner, useSigma, GraphCanvas, getNodeTypeIcon) with no evidence in the PR description or commits that gitnexus_impact was run on any of them, and no evidence that gitnexus_detect_changes() was executed before committing.


Additionally, 3 bugs were found inline (see review comments):

  1. Stale forces in multi-step physics loops (useSigma.ts) — forces computed once before the sub-steps loop cause doubled displacement when frames take >64ms.
  2. Cross-cutting edges silently dropped (graph-adapter.ts) — simple graph + hasEdge guard causes cross-cutting edges to be skipped when a hierarchy edge already exists between the same node pair, affecting both Tree View and Circles View.
  3. Vacuous test (graph-adapter.test.ts) — the test for cross-cutting edge styling passes with zero assertions due to bug Welcome to GitNexus Discussions! #2.

@hugogu hugogu force-pushed the feature/viewer-update branch from 2c72a71 to 5db5465 Compare May 25, 2026 14:46

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Found 4 inline issues (plus 1 general comment posted separately). Issues cover runtime bugs and CLAUDE.md workflow violations.

Comment thread gitnexus-web/src/components/FileTreePanel.tsx
Comment thread gitnexus-web/src/hooks/useSigma.ts Outdated
Comment thread gitnexus-web/src/lib/graph-adapter.ts Outdated
Comment thread gitnexus-web/src/lib/graph-adapter.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

CLAUDE.md violation: gitnexus_detect_changes() must be run before committing.

Per the CLAUDE.md GitNexus rules:

"MUST run gitnexus_detect_changes() before committing to verify your changes only affect expected symbols and execution flows."
"NEVER commit changes without running gitnexus_detect_changes() to check affected scope."

There is no evidence in the PR description or commit messages that gitnexus_detect_changes() was run. Please run it and confirm the affected scope is as expected before merging.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds two alternate graph layouts (tree/sequential and concentric circles/radial) to the web viewer, including a new view-mode state, UI toggle, layout/adaptation logic, and accompanying unit + E2E coverage to improve readability on larger repositories.

Changes:

  • Introduces deterministic initial layout generators for Tree and Circles views, plus Graphology adapters to seed Sigma with the new coordinates.
  • Adds graphViewMode to app state and a Force / Sequential / Radial toggle in the canvas UI, wiring the mode into useSigma layout execution.
  • Expands test coverage (Vitest + Playwright) and updates i18n strings and Vitest configuration to pick up new colocated tests.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
gitnexus-web/vitest.config.ts Extends Vitest include globs to pick up src/**/*.test.* tests.
gitnexus-web/test/unit/filter-panel.test.ts Updates icon mapping expectations for new node labels (Property/Const).
gitnexus-web/src/locales/en/graph.json Adds view-mode labels for the new toggle UI.
gitnexus-web/src/locales/zh-CN/graph.json Adds Chinese view-mode labels for the new toggle UI.
gitnexus-web/src/lib/tree-layout.ts New tree/sequential layout calculation (layered + proportional seeding + constrained relaxation).
gitnexus-web/src/lib/tree-layout.test.ts Unit tests validating tree layout layering, spread, ordering, and stability properties.
gitnexus-web/src/lib/circles-layout.ts New circles/radial layout calculation (ring assignment + parent-centered angular seeding).
gitnexus-web/src/lib/lucide-icons.tsx Re-exports the Network icon for the new view toggle.
gitnexus-web/src/lib/constants.ts Updates default visible/filterable labels to include Property/Const.
gitnexus-web/src/lib/graph-adapter.ts Adds Graphology adapters for tree/circles layouts and switches to MultiGraph to preserve multiple edges per pair.
gitnexus-web/src/lib/graph-adapter.test.ts Tests new adapters (layout presence, edge styling, and a performance budget check).
gitnexus-web/src/hooks/useSigma.ts Adds tree/circles physics loops + layout switching support, and edge styling tweaks based on hierarchy-ness.
gitnexus-web/src/hooks/useAppState.tsx Surfaces graphViewMode and its setter through app state.
gitnexus-web/src/hooks/app-state/graph.tsx Stores graphViewMode in the graph state context.
gitnexus-web/src/hooks/app-state/graph.test.tsx Unit tests for graphViewMode default and setter behavior.
gitnexus-web/src/components/GraphCanvas.tsx Adds the Force/Sequential/Radial toggle UI and rebuilds the Sigma graph based on view mode.
gitnexus-web/src/components/FileTreePanel.tsx Adds icon support for Property/Const nodes.
gitnexus-web/e2e/tree-view.spec.ts E2E coverage for switching layouts and basic interaction in sequential layout.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gitnexus-web/src/hooks/useSigma.ts Outdated
Comment thread gitnexus-web/src/hooks/useSigma.ts Outdated
Comment thread gitnexus-web/src/lib/graph-adapter.ts
Comment thread gitnexus-web/src/components/GraphCanvas.tsx
Comment thread gitnexus-web/src/lib/graph-adapter.test.ts Outdated
Add alternate graph layouts to the web viewer with new graph view state, canvas controls, adapters, and Sigma layout logic for tree and concentric-circle rendering. Include layout and adapter tests plus tree-view E2E coverage aligned with the English UI labels, and tune node visibility, edge layering, large-graph behavior, and tree-layer spacing so the new views stay readable. Follow up the tree-view work by keeping noisy variables hidden by default and mapping Property/Const icons so filter coverage stays in sync with the expanded node taxonomy.

Co-authored-by: OpenAI Codex <noreply@openai.com>
AI-model: GPT-5 Codex
hugogu and others added 8 commits May 26, 2026 21:03
Finding A (blocker): calculateTreeLayout runs 14 synchronous spring
iterations over all edges and nodes — O(N×E×14) + O(N log N) per layer
per iteration — with no size guard.  At 10K+ nodes this freezes the
main thread for several seconds.

Fix: make SPRING_ITERATIONS adaptive:
  - N > 10 000 → 0 iterations (proportional initial layout only)
  - N >  3 000 → 4 iterations
  - otherwise  → 14 iterations (unchanged behaviour for small graphs)

Also removes the unused `const r` at useSigma.ts:1314, which was a
leftover after the radial-resistance decomposition was removed.
This clears the CodeQL "unused variable" warning (Finding G).

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
…ound

Finding B (high): knowledgeGraphToCirclesGraphology had zero test
coverage.  Adds three new tests:

- ring placement: verifies Folder→ring 0, File→ring 1, Function→ring 3
  and confirms circles-specific attributes (circlesRing, circlesAnchorX/Y)
  are set while tree attributes (treeAnchorX/Y) are absent.
- edge styling: CONTAINS is marked isHierarchyEdge=true with the
  hierarchy colour; CALLS is cross-cutting with its own colour.
- CALLS cross-cutting: a lone CALLS edge between two Functions is
  correctly identified as a non-hierarchy edge.

Also adds a performance-bound test for the tree adapter at 2 000 nodes /
4 000 edges (the adaptive 14-iteration path) asserting completion within
2 s — catches regressions to the O(N×E×iterations) main-thread blocking
that Finding A identified.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
… Layout

Aligns the UI labels with standard graph layout terminology from the
Cambridge Intelligence taxonomy (cambridge-intelligence.com/blog/automatic-graph-layouts):

  Tree View   → Sequential Layout  (顺序布局)
  Circles     → Radial Layout      (径向布局)
  Force Graph → Force Graph        (unchanged)

Internal graphViewMode keys ('tree', 'circles', 'force') are unchanged —
only the displayed strings in en/zh-CN locales and the E2E button selectors
are updated.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
For graphs with N > 5 000 nodes, each rAF frame of runTreeLayout was
doing O(N log N) sort + O(N × k) repulsion pair comparisons (k ≈ 2 400
for a 20 K-node graph spread across 1 080 px at range 130).  At that
scale each frame took hundreds of ms, making the canvas appear completely
frozen even though the physics loop was still running.

Fix mirrors the circles layout adaptive strategy:

  N > 5 000 (large):
    - Skip repulsion pass (O(N × k) → 0)
    - Skip spread-force sort (O(N log N) → 0)
    - Velocity cap raised to ±12 / ±6 px so nodes cover ground faster
    - Damping 0.58, 1 sim step/frame, 30 s max duration
    - Looser early-stop thresholds (max v 0.05, avg v 0.03, active 2 %)

  N > 1 500 (medium):
    - Velocity cap raised to ±6 / ±3 px
    - 24 s max duration
    - Repulsion and spread still active

  N ≤ 1 500 (small):
    - Unchanged behaviour (velocity ±3/±2, 18 s, all forces active)

Layer gravity (O(N)) and edge springs (O(E)) run for all graph sizes —
they provide the structural pull that replaces repulsion at large N.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
…ection

The sigma 'clickNode', 'clickStage', 'enterNode', and 'leaveNode' handlers
are registered in a one-time useEffect (empty dep array). They captured
options.onNodeClick via closure, so they always called the initial version
of handleNodeClick — the one created before the graph loaded where
`if (!graph) return` exits immediately.

Consequence: clicking a node in the canvas never updated the app-level
selectedNode state. This broke:
  - The Focus Depth filter (warning "Select a node to apply depth filter"
    persisted even after a canvas click)
  - The depth hop filter not applying (selectedNode was always null)
  - The code panel not opening on canvas node click

Fix: store the three callback props in refs (onNodeClickRef, onNodeHoverRef,
onStageClickRef) and update them synchronously on every render. The sigma
event handlers now read from the refs, so they always invoke the latest
version of the callbacks without needing to re-register.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
Bug 1 (useSigma.ts): forces in the tree physics loop were computed once
before the sub-steps loop and reused for every step, causing 2× displacement
on slow frames (>64ms, simulationSteps>1). Fix: move forceX/forceY Maps and
all force accumulation (layer gravity, edge springs, repulsion, spread) inside
the loop so each sub-step integrates from current node positions.

Bug 2 (graph-adapter.ts): all three adapters used `graph.hasEdge(src,tgt)`
as a dedup guard, which silently drops any second edge between the same node
pair. A CALLS relationship between nodes that also have a CONTAINS edge was
always lost. Fix: switch from `new Graph()` to `new MultiGraph()` (allows
multiple edges per pair) and dedup by `rel.id` instead of by node pair.

Bug 3 (graph-adapter.test.ts): the cross-cutting edge styling test never
executed its CALLS branch because Bug 2 dropped the CALLS edge before the
assertion ran. Fix: assert `sigmaGraph.size === 2` and verify both edges
individually after collecting attrs by relationType.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-5
- Move radial layout force accumulation inside the sub-step loop so
  forces are recomputed from updated node positions each iteration
  instead of using stale forces computed before the loop began
- Revert knowledgeGraphToGraphology from MultiGraph back to Graph with
  node-pair deduplication to prevent ForceAtlas2 from double-applying
  spring forces for node pairs that share multiple relation types
- Add Target to the lucide-icons import in FileTreePanel.tsx so the
  Const node type icon resolves without a ReferenceError

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-6
Edge visibility (useSigma.ts): HAS_METHOD / HAS_PROPERTY edges were hidden
when any edge-type filter was active because those types are not in the EdgeType
union. Normalize HAS_METHOD → DEFINES and HAS_PROPERTY → CONTAINS before the
visibleTypes.includes() guard so Kotlin/Java hierarchy edges follow the same
filter logic as their semantic equivalents.

Force-mode edge styles (graph-adapter.ts): HAS_METHOD / HAS_PROPERTY fell back
to the default gray color in the force-graph adapter because EDGE_STYLES had no
entries for them. Added explicit entries using the same hues as DEFINES/CONTAINS
so force mode renders Kotlin/Java hierarchy edges consistently with tree/circles.

Accessibility (GraphCanvas.tsx, locales): the layout-mode switcher (Force /
Tree / Circles) had no ARIA semantics. Added role="tablist" on the container
and role="tab" + aria-selected on each button. Added the viewModes.label i18n
key (used as aria-label on the tablist) to en and zh-CN locale files.

Flaky test (graph-adapter.test.ts): replaced the hard 2 s wall-clock assertion
with a structural check (node count + edge count) that is deterministic across
CI hardware. Timing tests are inherently flaky and provide no correctness signal.

Co-authored-by: Claude <noreply@anthropic.com>
AI-model: claude-sonnet-4-5
@hugogu hugogu force-pushed the feature/viewer-update branch from 397e564 to 4e5df3d Compare May 26, 2026 13:06
@magyargergo magyargergo merged commit c8117d1 into abhigyanpatwari:main May 26, 2026
26 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitnexus-web: Redesign the graph explorer for better readability on larger repos

4 participants