diff --git a/quartz/components/Graph.tsx b/quartz/components/Graph.tsx index e159aa541..1b8071b93 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 dc5c99dc1..1aff138f2 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(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() @@ -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")