Furnic/graph view#14
Conversation
- Remove fitView prop from ReactFlow (was re-fitting on every node update) - Only fitView on initial layout, not on subsequent data changes - Use ref for fitView to avoid effect dependency loop
- Replace ELK layered algorithm with stress (force-directed) - Tighter node spacing, nodes cluster naturally around connections - Remove partition assignment (not needed for stress layout)
- Remove zoom-based adaptive labels (too noisy on dense graphs) - Only show label below the clicked/selected dot - Position detail popover near the clicked node instead of fixed bottom-left
- Use ELK mrtree algorithm for clean hierarchical tree layout - Replace straight edges + arrowheads with smoothstep curves - Remove arrowhead markers for cleaner look - Adaptive spacing based on node count
…tric rings - Replace ELK mrtree with custom radial layout algorithm - BFS from roots assigns depth, nodes placed in concentric rings - Ring spacing adapts to tree depth - Switch to bezier edges for organic curves - Drop elkjs dependency from layout (pure JS now)
- Each parent gets an angular wedge proportional to its subtree size - Children placed within parent's wedge, keeping them close - Fills space more evenly, avoids crossing edges - Straight edges while iterating on layout
…highlights in graph
…r, and color modes - nodeRadius from layout overrides entropy-based sizing - isCollapsed shows dashed border ring - colorMode: state (default), children (blue→red by child count), entropy (blue→red by subtree entropy)
… view - Depth pills (1/2/3/4/All) re-layout with maxDepth, collapsed nodes show dashed ring - Color mode toggle: State (default), Children (heatmap by child count), Entropy (heatmap by subtree entropy) - Pass layout-computed nodeRadius, isCollapsed, childCount, subtreeEntropy to DotNode - Edges filtered to only include visible (laid-out) nodes - Removed Re-layout button (depth change auto-relayouts)
- Use fixed base distance per hop (120px) instead of fraction of global radius - Smaller nodes (6-16px radius vs 8-20px) to reduce clutter - More overlap resolution iterations (20) - Children always placed at consistent distance from parent - Larger subtrees get only slight distance bonus (not 3x variation)
- Few children (1-3) stay close (60px), many children (12+) spread wider (150px) - Prevents sparse layouts at depth 1 with few nodes - Prevents cramped layouts at depth 2+ with many siblings
- Dot sits at top of flex column, use radius for Y offset instead of half height - Fixes edges connecting to wrong point when label is visible
…ructure changes - Use stable fingerprint of node IDs + edge IDs + maxDepth - Polling creates new array references every 2s but same content - Layout only re-runs when nodes/edges are actually added or removed
- 4-stop gradient: deep purple → cyan → yellow → red-orange - Log scaling so leaf nodes (0) are clearly distinct from 1-2 children - Much higher perceptual contrast on dark backgrounds
- Compute maxChildCount and maxSubtreeEntropy from actual data - Pass to DotNode so colors use full gradient range - Use sqrt scaling for perceptual evenness - Fixes entropy heatmap looking uniform when values are in narrow range
…dient spread - Compute min and max of both childCount and subtreeEntropy from dataset - Linear interpolation between min and max ensures full color range used - Fixes all nodes looking the same color when values cluster in narrow range
freesig
left a comment
There was a problem hiding this comment.
Left inline comments on two issues — see threads on GraphView.tsx lines 377 and 133.
| {selectedNode && popoverPos && ( | ||
| <div | ||
| className="absolute w-72 bg-gray-900 border border-gray-700 rounded-lg shadow-xl p-3 z-50" | ||
| style={{ left: popoverPos.x + 16, top: popoverPos.y - 8 }} |
There was a problem hiding this comment.
This popover position isn't clamped to the viewport. If you click a node near the right edge or bottom of the canvas, the 288px-wide popover will overflow off-screen and be partially or fully hidden.
Consider clamping against containerRef.current.getBoundingClientRect() so it stays within bounds — e.g. flip to the left of the cursor when there isn't room on the right, and above when near the bottom.
| searchQuery: string, | ||
| activeStates: Set<NodeState>, | ||
| ): boolean { | ||
| if (activeStates.size > 0 && !activeStates.has(node.state)) return false |
There was a problem hiding this comment.
The toggle semantics here can be confusing. When activeStates is empty (no pills toggled), everything is visible. When you toggle one pill (e.g. unanswered), it switches to "show only unanswered" — which hides states you didn't explicitly ask to hide.
Users are more likely to expect subtractive filtering: "I toggled unanswered, so hide unanswered nodes." The current behavior is additive ("show only these"), which is the opposite mental model.
Consider either:
- Inverting the logic — pills start active (all on), toggling one OFF fades that state, or
- Adding a visual cue (e.g. "Showing only: unanswered") so the current mode is clear.
|
|
||
| export type ColorMode = "state" | "children" | "entropy" | ||
|
|
||
| export interface DotNodeData { |
There was a problem hiding this comment.
STATE_COLORS is duplicated here and in GraphView.tsx. Consider extracting it to a shared constants file (e.g. graph-constants.ts) so they stay in sync.
| const q = searchQuery.toLowerCase() | ||
| if (!node.question.toLowerCase().includes(q) && !(node.answer?.toLowerCase().includes(q) ?? false)) return false | ||
| } | ||
| return true |
There was a problem hiding this comment.
proOptions={{ hideAttribution: true }} requires a React Flow Pro subscription. If you're on the free tier this flag is silently ignored and may log console warnings. Verify you have a valid license, or remove it and style around the attribution badge.
| @@ -0,0 +1,425 @@ | |||
| import { useState, useCallback, useEffect, useMemo, useRef } from "react" | |||
There was a problem hiding this comment.
We have e2e tests in web/e2e/ — would be good to add at least one e2e test that toggles the graph view on/off and verifies basic rendering (canvas appears, a node is visible, switching back to focus view works).
adds back a graph view with different filters and search.