feat(webui): tree view, interactive graph, URL-routed filters#42
Merged
Conversation
Splits the sidebar's search + sort + page-list mass out of App.tsx into
dedicated components so each piece owns its own state:
- search.tsx searchTokens / searchRegex / Highlighted helpers
- SortToggle.tsx SortMode type, sort cycle button + icons
- PageList.tsx flat list view (used by Recent and A→Z title)
- PageTree.tsx Outlook-style tree view (used by A→Z path);
owns its own collapsed-paths state, persisted to
localStorage as mm-tree-collapsed
- PageBrowser.tsx owns sortMode, sorts pages, renders search row +
SortToggle + either PageList or PageTree
App.tsx no longer carries sortMode, sortPages, SortIcon, the tree
builder, or any sidebar list rendering — it just hands rawPages to
PageBrowser with the relevant callbacks.
Tree rendering specifics:
- Folders derived from path segments; intermediate parents always
visible.
- Folders sort first within a level, then alphabetical by segment.
- Chevron toggles expansion; clicking a folder name that's also a
real page navigates; clicking a folder-only name toggles.
- Default state: all expanded; user collapses are remembered.
Replaces the empty 'select a page' state with an interactive
force-directed graph rendered by the force-graph library
(~50 KB canvas-based). Each node is a unique path prefix; nodes
that correspond to a real page are drawn larger and in the brand
accent color, intermediate folders are smaller and muted. Click
a page node to open it.
Two edge kinds, both togglable from an in-graph toolbar/legend
(state persisted via mm-graph-show-{paths,refs}):
- path edges thin, border-color, parent prefix → child
(so projects/mind-map/architecture produces
projects → projects/mind-map →
projects/mind-map/architecture)
- reference edges thicker, accent-color, with arrowhead,
page → wikilink target (sourced from the
links table via the new /api/links endpoint)
Labels are drawn above a zoom threshold so the graph stays
readable when fully zoomed out. Colors are read from CSS custom
properties so light/dark themes both work.
Backend additions:
- wiki.Link struct + Wiki.AllLinks(ctx) that dumps the links
table as a flat list
- GET /api/links handler
Polishes the graph view shipped earlier in this branch and ties it
into the rest of the app:
- Smaller nodes, labels drawn below the node with greedy word-wrap
at a max width so long titles don't run off horizontally.
- Theme-aware via a MutationObserver on <html>; cached colors are
refreshed and the canvas is repainted whenever the theme class
changes (no mouse-nudge needed).
- New CSS vars --graph-edge-path and --graph-edge-ref give path
and reference edges their own theme-tuned palette so both are
legible in light mode (where --border was washing path edges
out) and reference edges stand out from page-node fill color.
- Labels and the filter toolbar use the Inter font at weight 300
with Metro-style letter-spacing.
Sidebar title links to the graph: 'mind-map' in the header is now
an anchor that calls navigate(null), clearing the path and showing
the graph view.
Graph driven by app-level state, not its own fetch:
- GraphView now takes pages and searchQuery as props instead of
re-fetching /api/pages. /api/links is still fetched once for
reference edges.
- Search query filters the graph: nodes whose id/label/title
match any token survive, plus their ancestor folders so the
chain stays drawable. Edges survive only when both endpoints
do. Hides isolated reference edges to dropped nodes.
- Clicking a gray folder-only node commits a search filter
equal to that folder path, so clicks act as 'show this
subtree'.
URL state for filters (bookmarkable, back-navigable):
- Hash now encodes both path and query: #/projects/foo?q=arch.
- Live typing replaceState's the URL so back-stack stays clean.
- Explicit commits (Enter in the search box, clicking a gray
node) pushState — Back undoes the filter session.
- Hashchange listener restores both path and query on
back/forward.
- URL takes precedence over localStorage on first load.
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.
Big webui pass — a tree view for path-sorted browsing, an interactive graph view of the wiki, and URL-routed filters that make the whole thing bookmarkable.
Three commits
1. Outlook-style tree view for A→Z path sort + component split (
b336fce)App.tsx:search.tsx—searchTokens/searchRegex/HighlightedSortToggle.tsx—SortMode+ cycle button with iconsPageList.tsx— flat list (Recent and A→Z title)PageTree.tsx— Outlook-style tree (A→Z path), owns its own collapsed-paths set persisted tomm-tree-collapsedPageBrowser.tsx— ownssortMode, sorts pages, renders search + sort + the chosen list/tree/-separated segments, folders sort first within a level then alphabetical. Click a folder-name navigates if it's also a real page; clicking the chevron just toggles. Default everything-expanded; user's collapses persist across reloads.2. Interactive graph view of pages and links (
d27d33d)force-graph(~50 KB canvas) replaces the empty "select a page" state..mdexists) are larger and accent-colored; folder-only intermediates are smaller and muted.wiki.AllLinks(ctx)that dumps thelinkstable, plus aGET /api/linkshandler.3. Graph polish + URL-state routing (
7424055)<html>refreshes cached colors and triggers a canvas repaint when light/dark toggles.--graph-edge-pathand--graph-edge-refso both edge kinds stay legible in both themes (was:--borderwas washing path edges out in light mode).navigate(null)→ graph view.#/projects/foo?q=arch. Live typingreplaceStates; explicit commits (Enter, gray-node click)pushStateso Back undoes the filter session. Hashchange listener restores both path and query on back/forward. URL takes precedence over localStorage on first load.Bundle impact
~728 KB → ~858 KB minified (force-graph + d3-force). All other deltas are app code.
Files
webui/src/{search,SortToggle,PageList,PageTree,PageBrowser,GraphView}.tsxwiki.Link,wiki.AllLinks,GET /api/linkswebui/src/{App.tsx,styles.css,mcp.ts,index.html}