From 453936b5fa85f9c257c44c4c9d9b15cb958a406c Mon Sep 17 00:00:00 2001 From: Jimmy Cushnie Date: Wed, 2 Sep 2026 14:52:08 -0400 Subject: [PATCH] Make clicking a folder node select that folder Fixes #1 --- src/Main.ts | 36 +++++++++++++++++++++++- src/interfaces/AppWithInternalPlugins.ts | 7 +++++ src/interfaces/LeafRenderer.ts | 6 +++- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/interfaces/AppWithInternalPlugins.ts diff --git a/src/Main.ts b/src/Main.ts index a2b14a7..be147ea 100644 --- a/src/Main.ts +++ b/src/Main.ts @@ -1,6 +1,7 @@ import { Plugin, WorkspaceLeaf } from "obsidian"; import { GraphLeafWithCustomRenderer } from "interfaces/GraphLeafWithCustomRenderer"; import { RendererData } from "interfaces/RendererData"; +import { AppWithInternalPlugins } from "interfaces/AppWithInternalPlugins"; import { Nullable } from "types/Nullable"; import { Settings } from "interfaces/Settings"; import { GraphSidebarControls } from "./GraphSidebarControls"; @@ -48,11 +49,13 @@ export default class Folders2GraphPlugin extends Plugin { */ public override onunload(): void { this.__getLeavesOfTypeGraph().forEach((leaf) => { - // Restablish the original data setter in the render, then delete the custom on, then reload the leaf. + // Restablish the renderer's original data setter and node click handler, then delete the custom ones, then reload the leaf. if (leaf.view.renderer.originalSetData) { leaf.view.renderer.setData = leaf.view.renderer.originalSetData; + leaf.view.renderer.onNodeClick = leaf.view.renderer.originalOnNodeClick; delete leaf.view.renderer.originalSetData; + delete leaf.view.renderer.originalOnNodeClick; this.graphSidebarControls.removeAll(); leaf.view.unload(); leaf.view.load(); @@ -70,6 +73,7 @@ export default class Folders2GraphPlugin extends Plugin { leaves.forEach((leaf) => { if (leaf.view.getViewType() === "graph") { this.__injectDataInLeaf(leaf); + this.__injectNodeClickInLeaf(leaf); leaf.view.unload(); leaf.view.load(); setTimeout(() => { @@ -169,6 +173,36 @@ export default class Folders2GraphPlugin extends Plugin { }; } + /** + * Makes clicking a folder node reveal that folder in the file explorer, instead of + * running Obsidian's default tag search for it. + * @param leaf The graph leaf to render. + */ + private __injectNodeClickInLeaf(leaf: GraphLeafWithCustomRenderer): void { + const renderer = leaf.view.renderer; + + // Store the original click handler in another property, then override it with a custom one. + if (renderer.originalOnNodeClick == undefined) { + renderer.originalOnNodeClick = renderer.onNodeClick; + } + + renderer.onNodeClick = (event, id, type) => { + // Folder nodes are keyed by their path with a leading slash, eg. "/folder/subfolder". + // No file or tag node ID starts with a slash, so this check guarantees this is a folder node. + if (id.startsWith("/")) { + const folder = id === "/" ? this.app.vault.getRoot() : this.app.vault.getFolderByPath(id.slice(1)); + + if (folder) { + (this.app as AppWithInternalPlugins).internalPlugins + .getEnabledPluginById("file-explorer") + ?.revealInFolder(folder); + } + } else { + return renderer.originalOnNodeClick?.(event, id, type); + } + }; + } + /** * Get all leaves of type "graph". * @returns The leaves of type "graph". diff --git a/src/interfaces/AppWithInternalPlugins.ts b/src/interfaces/AppWithInternalPlugins.ts new file mode 100644 index 0000000..acd0ec6 --- /dev/null +++ b/src/interfaces/AppWithInternalPlugins.ts @@ -0,0 +1,7 @@ +import { App, TFolder } from "obsidian"; + +export type AppWithInternalPlugins = App & { + internalPlugins: { + getEnabledPluginById: (id: string) => { revealInFolder: (folder: TFolder) => void } | null; + }; +}; diff --git a/src/interfaces/LeafRenderer.ts b/src/interfaces/LeafRenderer.ts index f3cf58e..9d8c4f2 100644 --- a/src/interfaces/LeafRenderer.ts +++ b/src/interfaces/LeafRenderer.ts @@ -1,6 +1,10 @@ import { RendererData } from "./RendererData"; +type NodeClickHandler = (event: MouseEvent, id: string, type: string) => void; + export type LeafRenderer = { setData: (data: RendererData) => void; originalSetData?: (data: RendererData) => void; -}; \ No newline at end of file + onNodeClick?: NodeClickHandler; + originalOnNodeClick?: NodeClickHandler; +};