feat: openkb visualize — interactive 3D knowledge graph#103
Open
KylinMountain wants to merge 13 commits into
Open
feat: openkb visualize — interactive 3D knowledge graph#103KylinMountain wants to merge 13 commits into
KylinMountain wants to merge 13 commits into
Conversation
…spect, filter, flow)
…enkb-deck-neon #101 made openkb-deck-neon the default deck skill (creator.py DEFAULT_DECK_SKILL) but left this test asserting the old openkb-deck-editorial, so it has been red on main. Unrelated to the visualize feature on this branch.
- canvas was a replaced element with width:auto, so inset:0 was ignored and it stayed at its intrinsic backing-store size — a doubling feedback loop (clientWidth←backing, backing←clientWidth*DPR) that never matched the window and rendered soft on retina. Add width:100%/height:100% so the CSS box tracks the viewport and the DPR backing store is correct. - with 71 nodes / 800 edges every label drew at rest → unreadable pileup. Show labels only for the most-connected hubs at rest; reveal the rest on hover (node + neighbors), on selection, or when zoomed in (scale>1.4). Add a dark text shadow for legibility over the edge web.
A relative --kb-dir override (or relative default_kb config) left the output path relative; Path.as_uri() raises on relative paths, so `visualize --open` would crash after writing the HTML. resolve() first.
A dense KB (here 71 nodes / 800 edges, avg degree ~22) made the force-directed layout swing violently and never settle — hubs were pulled by ~22 springs at once, the summed force outran the damping, and nodes flew around illegibly. Legend filtering looked broken only because the motion masked it. Fixes: - degree-normalize spring pull (1/min(deg_a,deg_b)) so a hub doesn't feel N x the force - floor the repulsion distance so near-coincident nodes don't explode - cap per-frame speed (VMAX) and damp harder (0.9 -> 0.84) - simulated annealing: an alpha temperature cools to ~0 so physics freezes and the graph holds still; dragging a node or toggling a legend type reheats it to re-settle. Verified in-browser on the real KB: settles in ~1.5s, fully still by ~5s, and legend filtering now visibly re-packs the remaining nodes.
Upgrade the force-directed graph from 2D to 3D so it can be rotated: - nodes now carry a z coordinate; physics (repulsion, degree-normalized springs, origin pull, annealing, speed cap) all run in 3D - seed on a golden-angle sphere so 3D structure reads immediately - per-frame project(): yaw/pitch rotation + perspective projection, with near=larger discs and depth fog (far nodes dim/recede) - draw nodes far→near (painter's algorithm) so nearer ones occlude - drag the background to orbit (yaw/pitch); scroll still zooms; dragging a node moves it in the current view plane via unproject(); pick() is now screen-space so hover/click stay accurate at any angle - hover highlight + flow particles, click→glass panel, legend filter all preserved and verified in-browser on the real KB. Default zoom bumped (scale 1.35) so the sphere fills more of the canvas.
Address feedback that hovering flashed the scene and that exploring was hard: - smooth, eased highlight (per-node hl lerp) replaces the hard faded snap; non-neighbors dim gently (never to black) and the hover pulse/ glow are toned down — no more flicker - an open inspector now keeps its node's connections lit (focus = hover || selected), so you can study one node's links at leisure - orbit sensitivity halved (0.0045) — rotation is no longer twitchy - zoom range widened to 9x and anchored on the cursor, so you can drill into a local cluster - drag a node to pull it out; releasing pins it (dashed ring, won't spring back) so its links fan out for inspection; double-click releases - hint line updated to match
Auto-open after generating so plain `openkb visualize` just shows the graph; pass --no-open for headless/CI use. The HTML is still written to output/visualize/graph.html every run (latest snapshot, shareable). If a browser can't be launched, print a hint instead of failing.
- graph.html project(): clamp the perspective denominator (FOCAL+z2) so a node crossing the camera plane can't divide by ~0 → ±Infinity/NaN, which previously flung the node off-screen and (via centerOn) poisoned panX/panY until reload. - graph.html: require >4px of motion before a press counts as a drag, so a normal click (with sub-pixel jitter) inspects the node instead of pinning it. - visualize.py: reuse schema.PAGE_CONTENT_DIRS instead of a local _NODE_DIRS copy, and derive the fallback type so a new content dir can't KeyError. - visualize.py: read each wiki file once (cache text for the edge pass) instead of twice.
- build_graph: summaries have no `sources` field — their origin is `full_text` (e.g. sources/nvda-10q.md). Include it so summary nodes show their source in the inspector instead of 'none'. - graph.html cleanup from review: cache per-node color (drops a per-frame colorOf lookup), store adjacency as a Set (O(1) neighbour test, was O(deg) per node per frame), one degree-sort feeding both hubs and labels, a TAU constant for the seven full-circle arcs, and a corrected comment on the spring degree-softening (it intentionally only damps hub-hub edges).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
openkb visualizecommand — renders the wiki's[[wikilink]]graph as an interactive 3D knowledge graph: self-contained offline HTML, no LLM, pure data + template.type; search centers a node.--no-openfor headless); writesoutput/visualize/graph.htmleach run (latest snapshot, shareable).Architecture:
openkb/visualize.py(build_graph+render_html) ·openkb/templates/graph.html(self-contained canvas template) · a thinvisualizeCLI command. Reuseslintwikilink extraction +frontmatter.parse; the template ships via hatchling's default packaging.Also folds in a one-line fix to a deck-skill test that was already red on
main(unrelated to visualize — #101 changed the default deck skill without updating the test).Test plan
pytest— 810 passedruffclean on new files