From 14cbbdb8a2f69ebc51cd53a82b50206c543778b0 Mon Sep 17 00:00:00 2001
From: Oskar Manhart <52569953+oskardotglobal@users.noreply.github.com>
Date: Thu, 14 Sep 2023 05:55:59 +0200
Subject: [PATCH] feat: display tag in graph view (#466)

* feat: tags in graph view

* fix: revert changing graph forces

* fix: run prettier
---
 quartz/components/Graph.tsx               |  6 ++++
 quartz/components/scripts/graph.inline.ts | 40 ++++++++++++++++-------
 2 files changed, 35 insertions(+), 11 deletions(-)

diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx
index e159aa5..1b8071b 100644
--- a/quartz/components/Graph.tsx
+++ b/quartz/components/Graph.tsx
@@ -13,6 +13,8 @@ export interface D3Config {
   linkDistance: number
   fontSize: number
   opacityScale: number
+  removeTags: string[]
+  showTags: boolean
 }
 
 interface GraphOptions {
@@ -31,6 +33,8 @@ const defaultOptions: GraphOptions = {
     linkDistance: 30,
     fontSize: 0.6,
     opacityScale: 1,
+    showTags: true,
+    removeTags: [],
   },
   globalGraph: {
     drag: true,
@@ -42,6 +46,8 @@ const defaultOptions: GraphOptions = {
     linkDistance: 30,
     fontSize: 0.6,
     opacityScale: 1,
+    showTags: true,
+    removeTags: [],
   },
 }
 
diff --git a/quartz/components/scripts/graph.inline.ts b/quartz/components/scripts/graph.inline.ts
index dc5c99d..1aff138 100644
--- a/quartz/components/scripts/graph.inline.ts
+++ b/quartz/components/scripts/graph.inline.ts
@@ -42,20 +42,38 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
     linkDistance,
     fontSize,
     opacityScale,
+    removeTags,
+    showTags,
   } = JSON.parse(graph.dataset["cfg"]!)
 
   const data = await fetchData
 
   const links: LinkData[] = []
+  const tags: SimpleSlug[] = []
+
   const validLinks = new Set(Object.keys(data).map((slug) => simplifySlug(slug as FullSlug)))
+
   for (const [src, details] of Object.entries<ContentDetails>(data)) {
     const source = simplifySlug(src as FullSlug)
     const outgoing = details.links ?? []
+
     for (const dest of outgoing) {
       if (validLinks.has(dest)) {
         links.push({ source, target: dest })
       }
     }
+
+    if (showTags) {
+      const localTags = details.tags
+        .filter((tag) => !removeTags.includes(tag))
+        .map((tag) => simplifySlug(("tags/" + tag) as FullSlug))
+
+      tags.push(...localTags.filter((tag) => !tags.includes(tag)))
+
+      for (const tag of localTags) {
+        links.push({ source, target: tag })
+      }
+    }
   }
 
   const neighbourhood = new Set<SimpleSlug>()
@@ -76,14 +94,18 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
     }
   } else {
     Object.keys(data).forEach((id) => neighbourhood.add(simplifySlug(id as FullSlug)))
+    if (showTags) tags.forEach((tag) => neighbourhood.add(tag))
   }
 
   const graphData: { nodes: NodeData[]; links: LinkData[] } = {
-    nodes: [...neighbourhood].map((url) => ({
-      id: url,
-      text: data[url]?.title ?? url,
-      tags: data[url]?.tags ?? [],
-    })),
+    nodes: [...neighbourhood].map((url) => {
+      const text = url.startsWith("tags/") ? "#" + url.substring(5) : data[url]?.title ?? url
+      return {
+        id: url,
+        text: text,
+        tags: data[url]?.tags ?? [],
+      }
+    }),
     links: links.filter((l) => neighbourhood.has(l.source) && neighbourhood.has(l.target)),
   }
 
@@ -127,7 +149,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
     const isCurrent = d.id === slug
     if (isCurrent) {
       return "var(--secondary)"
-    } else if (visited.has(d.id)) {
+    } else if (visited.has(d.id) || d.id.startsWith("tags/")) {
       return "var(--tertiary)"
     } else {
       return "var(--gray)"
@@ -231,11 +253,7 @@ async function renderGraph(container: string, fullSlug: FullSlug) {
     .attr("dx", 0)
     .attr("dy", (d) => -nodeRadius(d) + "px")
     .attr("text-anchor", "middle")
-    .text(
-      (d) =>
-        data[d.id]?.title ||
-        (d.id.charAt(0).toUpperCase() + d.id.slice(1, d.id.length - 1)).replace("-", " "),
-    )
+    .text((d) => d.text)
     .style("opacity", (opacityScale - 1) / 3.75)
     .style("pointer-events", "none")
     .style("font-size", fontSize + "em")