Skip to content

feat(webui): tree view, interactive graph, URL-routed filters#42

Merged
aniongithub merged 3 commits into
mainfrom
feature/webui-enhancements
May 18, 2026
Merged

feat(webui): tree view, interactive graph, URL-routed filters#42
aniongithub merged 3 commits into
mainfrom
feature/webui-enhancements

Conversation

@aniongithub
Copy link
Copy Markdown
Owner

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)

  • Splits the sidebar's search-and-list mass out of App.tsx:
    • search.tsxsearchTokens/searchRegex/Highlighted
    • SortToggle.tsxSortMode + cycle button with icons
    • PageList.tsx — flat list (Recent and A→Z title)
    • PageTree.tsx — Outlook-style tree (A→Z path), owns its own collapsed-paths set persisted to mm-tree-collapsed
    • PageBrowser.tsx — owns sortMode, sorts pages, renders search + sort + the chosen list/tree
  • Tree behavior: folders derived from /-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.
  • Nodes: one per unique path prefix. Page nodes (real .md exists) are larger and accent-colored; folder-only intermediates are smaller and muted.
  • Two edge kinds with a togglable toolbar/legend (state persisted):
    • path — parent prefix → child prefix
    • reference — page → wikilink target, with an arrowhead
  • Backend: new wiki.AllLinks(ctx) that dumps the links table, plus a GET /api/links handler.

3. Graph polish + URL-state routing (7424055)

  • Smaller nodes, labels drawn below with greedy word-wrap.
  • Theme-aware: MutationObserver on <html> refreshes cached colors and triggers a canvas repaint when light/dark toggles.
  • New CSS vars --graph-edge-path and --graph-edge-ref so both edge kinds stay legible in both themes (was: --border was washing path edges out in light mode).
  • Sidebar title "mind-map" is now an anchor → navigate(null) → graph view.
  • GraphView is now driven by app state (pages + searchQuery as props), not its own fetch.
  • Search filter applies to the graph: nodes whose id/label/title match a token survive, plus their ancestor folders so the path chain stays drawable; edges survive only when both endpoints do.
  • Clicking a gray folder node commits a search equal to its full path — acts as "show this subtree".
  • URL state: hash now encodes both, e.g. #/projects/foo?q=arch. Live typing replaceStates; explicit commits (Enter, gray-node click) pushState so 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

  • New: webui/src/{search,SortToggle,PageList,PageTree,PageBrowser,GraphView}.tsx
  • New backend: wiki.Link, wiki.AllLinks, GET /api/links
  • Edits: webui/src/{App.tsx,styles.css,mcp.ts,index.html}

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.
@aniongithub aniongithub merged commit a2edffb into main May 18, 2026
1 check passed
@aniongithub aniongithub deleted the feature/webui-enhancements branch May 18, 2026 06:48
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.

1 participant