Color mode

Garden Explorer

Interactive note garden explorer: a local/global content graph, search box, tag/folder filters, and note details in a single page.

日本語

Overview

gardenExplorerPlugin() provides an interactive GardenExplorer component with three panels:

  • Explorer — search box plus optional tag and folder filter chips (with counts) and a filtered note list
  • Graph — an SVG graph of published notes and internal links with local and global modes, force or radial layout, hover neighbor emphasis, node drag, wheel/button zoom, canvas pan, reset, and click-to-open navigation
  • Details — the selected note's links, tags, excerpt, and related notes

Selection state is mirrored to the URL query (?note=, ?tag=, ?folder=), so the view is shareable and the filter list adapts to the current selection.

getGardenExplorerData() builds the note set from manifest.discoverableEntries and manifest.graph, including headings, a plain-text body (truncated to 4,000 chars), tags, folders, outgoing links, and backlinks. The graph only contains published/discoverable notes and resolved note links, so unpublished, excluded, or missing pages do not appear as graph nodes.

The note list in the Explorer panel shows the first 80 filtered notes for UI performance. The Global Graph uses all filtered notes. If a URL-selected note falls outside the first 80, it is preserved in the Global Graph.

Local and global graph

  • Local graph starts at the selected note and shows neighbors up to depth hops. The default is depth: 1, matching the common Obsidian/Quartz model of direct backlinks and outgoing links. Use depth: 0 to show only the selected note.
  • Global graph shows all currently filtered public notes and their published internal links.

This is intentionally close to Obsidian's exploration model, but it uses Riebeckite's Page System, public manifest, permalinks, and content graph instead of rescanning the vault in the browser.

Layouts

  • Force layout (layout: "force", default) uses a small deterministic built-in simulation: repulsion, link distance, centering, damping, and bounded stabilization. It adds no large dependency. With large graphs (>300 nodes) the force layout computation becomes noticeable; a warning is shown in the toolbar. For large Global Graphs, consider using the radial layout. Above 500 nodes, explicit user approval is required before the force layout runs.
  • Radial layout (layout: "radial") keeps the existing Riebeckite radial layout available for compact or deterministic presentations. It runs in near-linear time and handles thousands of nodes instantly.

Usage

ts
import { defineConfig } from "@riebeckite/core";
import { gardenExplorerPlugin } from "@riebeckite/plugin-garden-explorer";
 
export default defineConfig({
  // ...
  plugins: [gardenExplorerPlugin()],
});

With configuration:

ts
plugins: [
  gardenExplorerPlugin({
    layout: "force",
    depth: 1,
    showTags: true,
    showFolders: false,
    nodeSize: 1,
    linkDistance: 84,
    repulsion: 1800,
    showLabels: true,
  }),
];

gardenExplorerPlugin() registers the /explore page type and bundles style.css and its client hydrator into the app. A catch-all route using resolveRiebeckiteRoute() and pluginPageSsgParams() renders and emits it; no plugin-specific application route is needed. The page is server-rendered first, then the registered client entry hydrates its graph, filters, and URL state after loading.

Embed the component elsewhere

tsx
import GardenExplorer, {
  getGardenExplorerData,
} from "@riebeckite/plugin-garden-explorer";
import { config } from "../config";
import { content } from "../content";
import { getArticleTitle } from "../lib/article-title";
 
const manifest = await content.getManifest();
const data = getGardenExplorerData({
  manifest,
  config,
  resolveTitle: getArticleTitle,
  options: { layout: "radial", depth: 2 },
});
 
// ...in a site-owned component
return <GardenExplorer data={data} />;

The component is client-side interactive and expects window to be available in the browser. The server-rendered fallback still exposes the surrounding explorer/detail structure and links in semantic lists.

Data

getGardenExplorerData() returns GardenExplorerData:

  • notes — published notes sorted by title, with searchable fields plus folder, outgoing, and backlinks
  • edges — note-to-note graph edges between published notes
  • tags — tag counts, most frequent first
  • folders — folder counts, alphabetical; root-level notes are "Root"
  • options — resolved graph options used by the hydrated component

Exports

  • gardenExplorerPlugin(options?) — plugin factory
  • GardenExplorer — interactive explorer component (default export of components/garden-explorer.tsx)
  • getGardenExplorerData({ manifest, config, resolveTitle, options? }) — builds the explorer dataset
  • Types: GardenExplorerData, GardenExplorerEdge, GardenExplorerFolder, GardenExplorerGraphLayout, GardenExplorerGraphMode, GardenExplorerNote, GardenExplorerOptions, GardenExplorerPluginOptions, GardenExplorerTag

See also

History

1 changesCollapseExpand
1 + <!-- Generated from packages/plugins/garden-explorer/README.md. Do not edit this page directly; edit the package README and run `pnpm docs:sync`. -->
2 +
3 + # Garden Explorer
4 +
5 + Interactive note garden explorer: a local/global content graph, search box,
6 + tag/folder filters, and note details in a single page.
7 +
8 + [日本語](./garden-explorer.md)
9 +
10 + ## Overview
11 +
12 + `gardenExplorerPlugin()` provides an interactive `GardenExplorer` component with
13 + three panels:
14 +
15 + - **Explorer** — search box plus optional tag and folder filter chips (with
16 + counts) and a filtered note list
17 + - **Graph** — an SVG graph of published notes and internal links with local and
18 + global modes, force or radial layout, hover neighbor emphasis, node drag,
19 + wheel/button zoom, canvas pan, reset, and click-to-open navigation
20 + - **Details** — the selected note's links, tags, excerpt, and related notes
21 +
22 + Selection state is mirrored to the URL query (`?note=`, `?tag=`, `?folder=`),
23 + so the view is shareable and the filter list adapts to the current selection.
24 +
25 + `getGardenExplorerData()` builds the note set from `manifest.discoverableEntries`
26 + and `manifest.graph`, including headings, a plain-text body (truncated to 4,000
27 + chars), tags, folders, outgoing links, and backlinks. The graph only contains
28 + published/discoverable notes and resolved note links, so unpublished, excluded,
29 + or missing pages do not appear as graph nodes.
30 +
31 + The note list in the Explorer panel shows the first 80 filtered notes for UI
32 + performance. The Global Graph uses all filtered notes. If a URL-selected note
33 + falls outside the first 80, it is preserved in the Global Graph.
34 +
35 + ## Local and global graph
36 +
37 + - **Local graph** starts at the selected note and shows neighbors up to `depth`
38 + hops. The default is `depth: 1`, matching the common Obsidian/Quartz model of
39 + direct backlinks and outgoing links. Use `depth: 0` to show only the selected
40 + note.
41 + - **Global graph** shows all currently filtered public notes and their published
42 + internal links.
43 +
44 + This is intentionally close to Obsidian's exploration model, but it uses
45 + Riebeckite's Page System, public manifest, permalinks, and content graph instead
46 + of rescanning the vault in the browser.
47 +
48 + ## Layouts
49 +
50 + - **Force layout** (`layout: "force"`, default) uses a small deterministic
51 + built-in simulation: repulsion, link distance, centering, damping, and bounded
52 + stabilization. It adds no large dependency. With large graphs (>300 nodes) the
53 + force layout computation becomes noticeable; a warning is shown in the toolbar.
54 + For large Global Graphs, consider using the radial layout. Above 500 nodes,
55 + explicit user approval is required before the force layout runs.
56 + - **Radial layout** (`layout: "radial"`) keeps the existing Riebeckite radial
57 + layout available for compact or deterministic presentations. It runs in
58 + near-linear time and handles thousands of nodes instantly.
59 +
60 + ## Usage
61 +
62 + ```ts
63 + import { defineConfig } from "@riebeckite/core";
64 + import { gardenExplorerPlugin } from "@riebeckite/plugin-garden-explorer";
65 +
66 + export default defineConfig({
67 + // ...
68 + plugins: [gardenExplorerPlugin()],
69 + });
70 + ```
71 +
72 + With configuration:
73 +
74 + ```ts
75 + plugins: [
76 + gardenExplorerPlugin({
77 + layout: "force",
78 + depth: 1,
79 + showTags: true,
80 + showFolders: false,
81 + nodeSize: 1,
82 + linkDistance: 84,
83 + repulsion: 1800,
84 + showLabels: true,
85 + }),
86 + ];
87 + ```
88 +
89 + `gardenExplorerPlugin()` registers the `/explore` page type and bundles
90 + `style.css` and its client hydrator into the app. A catch-all route using
91 + `resolveRiebeckiteRoute()` and `pluginPageSsgParams()` renders and emits it;
92 + no plugin-specific application route is needed. The page is server-rendered
93 + first, then the registered client entry hydrates its graph, filters, and URL
94 + state after loading.
95 +
96 + ### Embed the component elsewhere
97 +
98 + ```tsx
99 + import GardenExplorer, {
100 + getGardenExplorerData,
101 + } from "@riebeckite/plugin-garden-explorer";
102 + import { config } from "../config";
103 + import { content } from "../content";
104 + import { getArticleTitle } from "../lib/article-title";
105 +
106 + const manifest = await content.getManifest();
107 + const data = getGardenExplorerData({
108 + manifest,
109 + config,
110 + resolveTitle: getArticleTitle,
111 + options: { layout: "radial", depth: 2 },
112 + });
113 +
114 + // ...in a site-owned component
115 + return <GardenExplorer data={data} />;
116 + ```
117 +
118 + The component is client-side interactive and expects `window` to be available in
119 + the browser. The server-rendered fallback still exposes the surrounding
120 + explorer/detail structure and links in semantic lists.
121 +
122 + ## Data
123 +
124 + `getGardenExplorerData()` returns `GardenExplorerData`:
125 +
126 + - `notes` — published notes sorted by title, with searchable fields plus
127 + `folder`, `outgoing`, and `backlinks`
128 + - `edges` — note-to-note graph edges between published notes
129 + - `tags` — tag counts, most frequent first
130 + - `folders` — folder counts, alphabetical; root-level notes are `"Root"`
131 + - `options` — resolved graph options used by the hydrated component
132 +
133 + ## Exports
134 +
135 + - `gardenExplorerPlugin(options?)` — plugin factory
136 + - `GardenExplorer` — interactive explorer component (default export of
137 + `components/garden-explorer.tsx`)
138 + - `getGardenExplorerData({ manifest, config, resolveTitle, options? })` — builds
139 + the explorer dataset
140 + - Types: `GardenExplorerData`, `GardenExplorerEdge`, `GardenExplorerFolder`,
141 + `GardenExplorerGraphLayout`, `GardenExplorerGraphMode`, `GardenExplorerNote`,
142 + `GardenExplorerOptions`, `GardenExplorerPluginOptions`, `GardenExplorerTag`
143 +
144 + ## See also
145 +
146 + - [Plugin guide](../reference/plugin-api.en.md)
147 + - [`@riebeckite/plugin-local-graph`](./local-graph.en.md)
148 +