From 6edc732dcc537c219e7b43f0437165319ea1ffec Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 10:52:37 +0600 Subject: [PATCH 01/11] graph revamp --- src/helpers/linkUtils.js | 160 +++++------ src/site/_includes/components/graphScript.njk | 249 +++++++++--------- src/site/_includes/components/pageheader.njk | 2 +- src/site/_includes/components/sidebar.njk | 15 ++ src/site/graph.11tydata.js | 53 ++++ src/site/graph.njk | 5 + src/site/index.11tydata.js | 93 +++---- src/site/notes/notes.11tydata.js | 33 ++- src/site/styles/obsidian-base.scss | 82 +++++- 9 files changed, 408 insertions(+), 284 deletions(-) create mode 100644 src/site/graph.11tydata.js create mode 100644 src/site/graph.njk diff --git a/src/helpers/linkUtils.js b/src/helpers/linkUtils.js index 6f6943d..630dfe1 100644 --- a/src/helpers/linkUtils.js +++ b/src/helpers/linkUtils.js @@ -1,108 +1,72 @@ -const wikilink = /\[\[(.*?\|.*?)\]\]/g +const wikiLinkRegex = /\[\[(.*?\|.*?)\]\]/g; const internalLinkRegex = /href="\/(.*?)"/g; - -function caselessCompare(a, b) { - return a.toLowerCase() === b.toLowerCase(); -} - function extractLinks(content) { - return[...(content.match(wikilink) || []).map(link => ( - link.slice(2, -2) - .split("|")[0] - .replace(/.(md|markdown)\s?$/i, "") - .replace("\\", "") - .trim() - )), ...(content.match(internalLinkRegex) || []).map( - (link) => - link - .slice(6, -1) - .split("|")[0] - .replace(/.(md|markdown)\s?$/i, "") - .replace("\\", "") - .trim() - )]; + return [ + ...(content.match(wikiLinkRegex) || []).map( + (link) => + link + .slice(2, -2) + .split("|")[0] + .replace(/.(md|markdown)\s?$/i, "") + .replace("\\", "") + .trim() + .split("#")[0] + ), + ...(content.match(internalLinkRegex) || []).map( + (link) => + link + .slice(6, -1) + .split("|")[0] + .replace(/.(md|markdown)\s?$/i, "") + .replace("\\", "") + .trim() + .split("#")[0] + ), + ]; } -function getBacklinks(data) { - const notes = data.collections.note; - if (!notes) { - return []; - } - const currentFileSlug = data.page.filePathStem.replace('/notes/', ''); - const currentURL = data.page.url; - - let backlinks = []; - let uniqueLinks = new Set(); - let counter = 1; - - for (const otherNote of notes) { - const noteContent = otherNote.template.frontMatter.content; - const backLinks = extractLinks(noteContent); - - if (!uniqueLinks.has(otherNote.url) && backLinks.some(link => caselessCompare(link, currentFileSlug) || - currentURL == link.split("#")[0])) { - let preview = noteContent.slice(0, 240); - backlinks.push({ - url: otherNote.url, - title: otherNote.data.title || otherNote.data.page.fileSlug, - preview, - id: counter++ - }) - uniqueLinks.add(otherNote.url); - } - } - return backlinks; +function shuffle(a) { + var j, x, i; + for (i = a.length - 1; i > 0; i--) { + j = Math.floor(Math.random() * (i + 1)); + x = a[i]; + a[i] = a[j]; + a[j] = x; + } + return a; } -function getOutboundLinks(data, isHome=false){ - const notes = data.collections.note; - - - - if (!notes || notes.length == 0) { - return []; - } - - let currentNote; - if (isHome) { - currentNote = data.collections.gardenEntry && data.collections.gardenEntry[0]; - } else { - const currentFileSlug = data.page.filePathStem.replace('/notes/', ''); - currentNote = notes.find(x => x.data.page.filePathStem && caselessCompare(x.data.page.filePathStem.replace('/notes/', ''), currentFileSlug)); - } - - if (!currentNote) { - return []; - } - - let counter = 1; - let uniqueLinks = new Set(); - - const outboundLinks = extractLinks(currentNote.template.frontMatter.content); - let outbound = outboundLinks.map(fileslug => { - var outboundNote = notes.find(x => caselessCompare(x.data.page.filePathStem.replace("/notes/", ""), fileslug) || x.data.page.url == fileslug.split("#")[0]); - if (!outboundNote) { - return null; - } - if (!uniqueLinks.has(outboundNote.url)) { - - uniqueLinks.add(outboundNote.url); - return { - url: outboundNote.url, - title: outboundNote.data.title || outboundNote.data.page.fileSlug, - id: counter++, - }; - } else { - return null; - } - }).filter(x => x); - return outbound; +function sliceIntoChunks(arr, chunkSize) { + const res = []; + for (let i = 0; i < arr.length; i += chunkSize) { + const chunk = arr.slice(i, i + chunkSize); + res.push(chunk); + } + return res; } -exports.wikilink = wikilink; +function getPositions(trees) { + let minInRow = Math.floor(Math.sqrt(trees.length)); + let maxInRow = Math.ceil(Math.sqrt(trees.length)); + if (minInRow < maxInRow) { + trees = trees.concat( + Array(Math.pow(maxInRow, 2) - trees.length).fill([0, "", ""]) + ); + } + trees = shuffle([...trees]); + let levels = sliceIntoChunks(trees, maxInRow); + return levels; +} + +function forestData(data) { + const canvasTrees = data.collections.note.map((n) => { + return [n.data.maturity || 1, n.url, n.data.title || n.fileSlug]; + }); + return getPositions(canvasTrees); +} + +exports.wikiLinkRegex = wikiLinkRegex; exports.internalLinkRegex = internalLinkRegex; -exports.getBacklinks = getBacklinks; -exports.getOutboundLinks = getOutboundLinks; -exports.caselessCompare = caselessCompare; -exports.extractLinks = extractLinks; \ No newline at end of file +exports.extractLinks = extractLinks; +exports.forestData = forestData; diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index 831886b..3dd645b 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -1,133 +1,138 @@ \ No newline at end of file diff --git a/src/site/_includes/components/pageheader.njk b/src/site/_includes/components/pageheader.njk index 0d400ba..4e3fe30 100644 --- a/src/site/_includes/components/pageheader.njk +++ b/src/site/_includes/components/pageheader.njk @@ -17,7 +17,7 @@ - + diff --git a/src/site/_includes/components/sidebar.njk b/src/site/_includes/components/sidebar.njk index b3cfd48..af033bd 100644 --- a/src/site/_includes/components/sidebar.njk +++ b/src/site/_includes/components/sidebar.njk @@ -6,6 +6,21 @@
Connected Pages
+
+
+ +
+ + + + + + +
+ +
+ +
diff --git a/src/site/graph.11tydata.js b/src/site/graph.11tydata.js new file mode 100644 index 0000000..973412d --- /dev/null +++ b/src/site/graph.11tydata.js @@ -0,0 +1,53 @@ +const { extractLinks } = require("../helpers/linkUtils"); + +module.exports = { + eleventyComputed: { + graphData: (data) => { + let nodes = {}; + let links = []; + let stemURLs = {}; + data.collections.note.forEach((v, idx) => { + let fpath = v.filePathStem.replace("/notes/", ""); + let parts = fpath.split("/"); + let group = "none"; + if (parts.length >= 3) { + group = parts[parts.length - 2]; + } + nodes[v.url] = { + id: idx, + title: v.data.title || v.fileSlug, + url: v.url, + group, + home: v.data["dg-home"] || false, + outBound: extractLinks(v.template.frontMatter.content), + neighbors: new Set(), + }; + stemURLs[fpath] = v.url; + }); + Object.values(nodes).forEach((node) => { + let outBound = new Set(); + node.outBound.forEach((olink) => { + let link = (stemURLs[olink] || olink).split("#")[0]; + outBound.add(link); + }); + node.outBound = Array.from(outBound); + node.outBound.forEach((link) => { + let n = nodes[link]; + if (n) { + n.neighbors.add(node.url); + node.neighbors.add(n.url); + links.push({ source: node.id, target: n.id }); + } + }); + }); + Object.keys(nodes).map((k) => { + nodes[k].neighbors = Array.from(nodes[k].neighbors); + nodes[k].size = nodes[k].neighbors.length; + }); + return JSON.stringify({ + nodes, + links, + }); + }, + }, +}; diff --git a/src/site/graph.njk b/src/site/graph.njk new file mode 100644 index 0000000..9a24ca0 --- /dev/null +++ b/src/site/graph.njk @@ -0,0 +1,5 @@ +--- +permalink: /graph +eleventyExcludeFromCollections: true +--- +{{ graphData | safe }} diff --git a/src/site/index.11tydata.js b/src/site/index.11tydata.js index 396fe9f..2a5974f 100644 --- a/src/site/index.11tydata.js +++ b/src/site/index.11tydata.js @@ -1,55 +1,60 @@ - require("dotenv").config(); const settings = require("../helpers/constants"); const markdownIt = require("markdown-it"); -const { getBacklinks, getOutboundLinks } = require("../helpers/linkUtils"); const md = markdownIt({ - html: true, + html: true, }).use(require("../helpers/utils").namedHeadingsFilter); const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { - eleventyComputed: { - backlinks: (data) => getBacklinks(data), - outbound: (data) => getOutboundLinks(data, true), - settings: (data) => { - const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; - if (currentnote && currentnote.data) { - const noteSettings = {}; - allSettings.forEach(setting => { - let noteSetting = currentnote.data[setting]; - let globalSetting = process.env[setting]; + eleventyComputed: { + settings: (data) => { + const currentnote = + data.collections.gardenEntry && data.collections.gardenEntry[0]; + if (currentnote && currentnote.data) { + const noteSettings = {}; + allSettings.forEach((setting) => { + let noteSetting = currentnote.data[setting]; + let globalSetting = process.env[setting]; - let settingValue = (noteSetting || (globalSetting === 'true' && noteSetting !== false)); - noteSettings[setting] = settingValue; - }); - return noteSettings; - - } - return {}; - }, - noteTitle: (data) => { - const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; - if (currentnote && currentnote.data) { - return currentnote.data.title || currentnote.data.page.fileSlug; - } - return ""; - }, - tags: (data) => { - const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; - if (currentnote && currentnote.data) { - return currentnote.data.tags; - } - return []; - }, - content: (data) => { - const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; - if (currentnote && currentnote.template && currentnote.template.frontMatter && currentnote.template.frontMatter.content) { - return md.render(currentnote.template.frontMatter.content); - } - return ""; - } - } -} \ No newline at end of file + let settingValue = + noteSetting || (globalSetting === "true" && noteSetting !== false); + noteSettings[setting] = settingValue; + }); + return noteSettings; + } + return {}; + }, + noteTitle: (data) => { + const currentnote = + data.collections.gardenEntry && data.collections.gardenEntry[0]; + if (currentnote && currentnote.data) { + return currentnote.data.title || currentnote.data.page.fileSlug; + } + return ""; + }, + tags: (data) => { + const currentnote = + data.collections.gardenEntry && data.collections.gardenEntry[0]; + if (currentnote && currentnote.data) { + return currentnote.data.tags; + } + return []; + }, + content: (data) => { + const currentnote = + data.collections.gardenEntry && data.collections.gardenEntry[0]; + if ( + currentnote && + currentnote.template && + currentnote.template.frontMatter && + currentnote.template.frontMatter.content + ) { + return md.render(currentnote.template.frontMatter.content); + } + return ""; + }, + }, +}; diff --git a/src/site/notes/notes.11tydata.js b/src/site/notes/notes.11tydata.js index 004846b..c036a74 100644 --- a/src/site/notes/notes.11tydata.js +++ b/src/site/notes/notes.11tydata.js @@ -1,24 +1,21 @@ - require("dotenv").config(); const settings = require("../../helpers/constants"); -const { getBacklinks, getOutboundLinks } = require("../../helpers/linkUtils"); -const allSettings = settings.ALL_NOTE_SETTINGS; +const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { - eleventyComputed: { - backlinks: (data) => getBacklinks(data), - outbound: (data) => getOutboundLinks(data), - settings: (data) => { - const noteSettings = {}; - allSettings.forEach(setting => { - let noteSetting = data[setting]; - let globalSetting = process.env[setting]; + eleventyComputed: { + settings: (data) => { + const noteSettings = {}; + allSettings.forEach((setting) => { + let noteSetting = data[setting]; + let globalSetting = process.env[setting]; - let settingValue = (noteSetting || (globalSetting === 'true' && noteSetting !== false)); - noteSettings[setting] = settingValue; - }); - return noteSettings; - } - } -} \ No newline at end of file + let settingValue = + noteSetting || (globalSetting === "true" && noteSetting !== false); + noteSettings[setting] = settingValue; + }); + return noteSettings; + }, + }, +}; diff --git a/src/site/styles/obsidian-base.scss b/src/site/styles/obsidian-base.scss index 328387a..d743e80 100644 --- a/src/site/styles/obsidian-base.scss +++ b/src/site/styles/obsidian-base.scss @@ -7943,6 +7943,8 @@ body { .graph-control-section.mod-display .setting-item:not(.mod-slider):last-child .setting-item-info { display: none; } + + .workspace-leaf-content[data-type='outline'] .view-content { padding: 0; } @@ -10632,4 +10634,82 @@ body { width: 100%; } - \ No newline at end of file + // Graph Controls + .graph-title-container { + position: relative; +} + +#full-graph { + position: fixed; + top: 50%; + left: 50%; + height: 60vh; + width: 60vw; + min-height: 400px; + min-width: 400px; + transform: translate(-50%, -50%); + z-index: 9999; + display: none; + background-color: var(--background-secondary); + + #full-graph-close { + position: absolute; + top: 10px; + right: 10px; + cursor: pointer; + z-index: 9; + } +} + +#full-graph.show { + display: block; +} + +#graph-controls { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 5px; + position: absolute; + top: 115%; + cursor: pointer; + right: 0px; + left: 10px; + color: var(--text-accent); + z-index: 9; + + .depth-control { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 7px; + + .slider { + datalist { + display: flex; + flex-direction: row; + justify-content: space-between; + font-size: 0.6rem; + // padding: 2px 0px; + // width: 200px; + } + + option { + padding: 0; + } + } + + #depth-display { + background-color: var(--text-accent); + color: white; + width: 1rem; + height: 1rem; + font-size: 0.8rem; + display: flex; + justify-content: center; + align-items: center; + margin-top: 0.3rem; + } + } +} \ No newline at end of file From 7994a930154aecf7a6c61aa767b7f99c54d218c6 Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 14:21:03 +0600 Subject: [PATCH 02/11] some enhancements --- src/site/_includes/components/graphScript.njk | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index 3dd645b..505275d 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -49,13 +49,17 @@ var Graph; - function renderGraph(graphData, id, width, height) { + function renderGraph(graphData, id, width, height, delay) { let Graph = ForceGraph() (document.getElementById(id)) .graphData(graphData) .nodeId('id') .nodeLabel('title') .linkSource('source') + .linkTarget('target') + .d3AlphaDecay(0.10) + .width(width) + .height(height) .linkDirectionalArrowLength(2) .linkDirectionalArrowRelPos(0.5) .nodeCanvasObject((node, ctx) => { @@ -79,8 +83,10 @@ .linkColor(() => getCssVar("--text-muted") || getCssVar("--text-normal")) .onNodeClick(node => { window.location = node.url; - }) - .linkTarget('target').width(width).height(height); + }); + setTimeout(() => { + Graph.zoomToFit(5, 75); + }, delay); return Graph; } @@ -102,7 +108,7 @@ Graph._destructor(); Graph = null; } - renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330); + renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, 1); }) window.fullGraph = null; @@ -118,8 +124,7 @@ g.classList.add('show'); document.body.appendChild(g); g.innerHTML = '
'; - - window.fullGraph = renderGraph(graphData, "full-graph-container", g.offsetWidth, g.offsetHeight); + window.fullGraph = renderGraph(graphData, "full-graph-container", g.offsetWidth, g.offsetHeight, 200); document.getElementById('full-graph-close').addEventListener('click', (evt) => { g.classList.remove('show'); window.fullGraph._destructor(); From ce494682cf5b9a499fa0fbc2e171f1ced505ac4c Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 15:00:17 +0600 Subject: [PATCH 03/11] distinguish current pages node --- src/site/_includes/components/graphScript.njk | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index 505275d..166b34b 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -30,6 +30,7 @@ let home = Object.values(remaining).find((v) => v.home); delete remaining[home.url]; } + currentNode.current = true; let existing = {}; existing[currentNode.url] = currentNode; for (let i = 0; i < window.maxGraphDepth; i++) { @@ -62,25 +63,33 @@ .height(height) .linkDirectionalArrowLength(2) .linkDirectionalArrowRelPos(0.5) - .nodeCanvasObject((node, ctx) => { - const numberOfLinks = (node.outBound && node.outBound.length) || 2; - const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2; - const nodeR = Math.min(7, Math.max((numberOfLinks + numberOfNeighbours) / 2, 2)); - ctx.beginPath(); - ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false); - ctx.fillStyle = getCssVar("--text-accent"); - ctx.fill(); - - const label = htmlDecode(node.title) - const fontSize = 3.5; - ctx.font = `${fontSize}px Sans-Serif`; - - ctx.textAlign = 'center'; - ctx.textBaseline = 'top'; - ctx.fillText(label, node.x, node.y + nodeR + 2); - }) - .linkColor(() => getCssVar("--text-muted") || getCssVar("--text-normal")) + .nodeCanvasObject((node, ctx) => { + const color = getCssVar("--text-accent"); + const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2; + const nodeR = Math.min(7, Math.max(numberOfNeighbours / 2, 2)); + + ctx.beginPath(); + ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false); + ctx.fillStyle = color; + ctx.fill(); + + if (node.current) { + ctx.beginPath(); + ctx.arc(node.x, node.y, nodeR + 1, 0, 2 * Math.PI, false); + ctx.lineWidth = 0.5; + ctx.strokeStyle = color; + ctx.stroke(); + } + + const label = htmlDecode(node.title) + const fontSize = 3.5; + ctx.font = `${fontSize}px Sans-Serif`; + + ctx.textAlign = 'center'; + ctx.textBaseline = 'top'; + ctx.fillText(label, node.x, node.y + nodeR + 2); + }) .onNodeClick(node => { window.location = node.url; }); From 24d7bdce5bf6cddfe5fdb906980743e836f3f24a Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 15:05:39 +0600 Subject: [PATCH 04/11] local graph zoom fix --- src/site/_includes/components/graphScript.njk | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index 166b34b..ed0610d 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -93,9 +93,11 @@ .onNodeClick(node => { window.location = node.url; }); - setTimeout(() => { - Graph.zoomToFit(5, 75); - }, delay); + if (delay != null) { + setTimeout(() => { + Graph.zoomToFit(5, 75); + }, delay); + } return Graph; } @@ -117,7 +119,7 @@ Graph._destructor(); Graph = null; } - renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, 1); + renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, null); }) window.fullGraph = null; From 3bb6041df03f59c8188228895fd303b2583c377d Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 16:14:50 +0600 Subject: [PATCH 05/11] fixed backlink --- src/helpers/linkUtils.js | 76 +++++++++++++++++--------------- src/site/index.11tydata.js | 3 +- src/site/notes/notes.11tydata.js | 2 + 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/helpers/linkUtils.js b/src/helpers/linkUtils.js index 630dfe1..c563250 100644 --- a/src/helpers/linkUtils.js +++ b/src/helpers/linkUtils.js @@ -1,6 +1,10 @@ const wikiLinkRegex = /\[\[(.*?\|.*?)\]\]/g; const internalLinkRegex = /href="\/(.*?)"/g; +function caselessCompare(a, b) { + return a.toLowerCase() === b.toLowerCase(); +} + function extractLinks(content) { return [ ...(content.match(wikiLinkRegex) || []).map( @@ -26,47 +30,47 @@ function extractLinks(content) { ]; } -function shuffle(a) { - var j, x, i; - for (i = a.length - 1; i > 0; i--) { - j = Math.floor(Math.random() * (i + 1)); - x = a[i]; - a[i] = a[j]; - a[j] = x; +function getBacklinks(data) { + const notes = data.collections.note; + if (!notes) { + return []; } - return a; -} + const currentFileSlug = data.page.filePathStem + .replace("/notes/", "") + .split("#")[0]; + const currentURL = data.page.url; -function sliceIntoChunks(arr, chunkSize) { - const res = []; - for (let i = 0; i < arr.length; i += chunkSize) { - const chunk = arr.slice(i, i + chunkSize); - res.push(chunk); + let backlinks = []; + let uniqueLinks = new Set(); + let counter = 1; + + for (const otherNote of notes) { + const noteContent = otherNote.template.frontMatter.content; + const backLinks = extractLinks(noteContent); + + if ( + !uniqueLinks.has(otherNote.url) && + backLinks.some( + (link) => + caselessCompare(link, currentFileSlug) || + currentURL == link.split("#")[0] + ) + ) { + let preview = noteContent.slice(0, 240); + backlinks.push({ + url: otherNote.url, + title: otherNote.data.title || otherNote.data.page.fileSlug, + preview, + id: counter++, + isHome: otherNote.data["dg-home"] || false, + }); + uniqueLinks.add(otherNote.url); + } } - return res; -} - -function getPositions(trees) { - let minInRow = Math.floor(Math.sqrt(trees.length)); - let maxInRow = Math.ceil(Math.sqrt(trees.length)); - if (minInRow < maxInRow) { - trees = trees.concat( - Array(Math.pow(maxInRow, 2) - trees.length).fill([0, "", ""]) - ); - } - trees = shuffle([...trees]); - let levels = sliceIntoChunks(trees, maxInRow); - return levels; -} - -function forestData(data) { - const canvasTrees = data.collections.note.map((n) => { - return [n.data.maturity || 1, n.url, n.data.title || n.fileSlug]; - }); - return getPositions(canvasTrees); + return backlinks; } exports.wikiLinkRegex = wikiLinkRegex; exports.internalLinkRegex = internalLinkRegex; exports.extractLinks = extractLinks; -exports.forestData = forestData; +exports.getBacklinks = getBacklinks; diff --git a/src/site/index.11tydata.js b/src/site/index.11tydata.js index 2a5974f..164a9e4 100644 --- a/src/site/index.11tydata.js +++ b/src/site/index.11tydata.js @@ -5,11 +5,12 @@ const markdownIt = require("markdown-it"); const md = markdownIt({ html: true, }).use(require("../helpers/utils").namedHeadingsFilter); - +const { getBacklinks } = require("../helpers/linkUtils"); const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { eleventyComputed: { + backlinks: (data) => getBacklinks(data), settings: (data) => { const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; diff --git a/src/site/notes/notes.11tydata.js b/src/site/notes/notes.11tydata.js index c036a74..f3b4049 100644 --- a/src/site/notes/notes.11tydata.js +++ b/src/site/notes/notes.11tydata.js @@ -1,10 +1,12 @@ require("dotenv").config(); const settings = require("../../helpers/constants"); +const { getBacklinks } = require("../../helpers/linkUtils"); const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { eleventyComputed: { + backlinks: (data) => getBacklinks(data), settings: (data) => { const noteSettings = {}; allSettings.forEach((setting) => { From 5fcee3da0b95bd65dd00db572207e5f51abbe7e0 Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Thu, 2 Feb 2023 10:41:46 +0600 Subject: [PATCH 06/11] single computation for both sidebar page mentions and graph --- src/helpers/linkUtils.js | 92 +++++++++++-------- src/site/_data/eleventyComputed.js | 5 + src/site/_includes/components/graphScript.njk | 2 +- src/site/_includes/components/sidebar.njk | 38 +++++--- src/site/graph.11tydata.js | 53 ----------- src/site/graph.njk | 4 +- src/site/index.11tydata.js | 2 - src/site/notes/notes.11tydata.js | 2 - 8 files changed, 89 insertions(+), 109 deletions(-) create mode 100644 src/site/_data/eleventyComputed.js delete mode 100644 src/site/graph.11tydata.js diff --git a/src/helpers/linkUtils.js b/src/helpers/linkUtils.js index c563250..d09e226 100644 --- a/src/helpers/linkUtils.js +++ b/src/helpers/linkUtils.js @@ -30,47 +30,63 @@ function extractLinks(content) { ]; } -function getBacklinks(data) { - const notes = data.collections.note; - if (!notes) { - return []; - } - const currentFileSlug = data.page.filePathStem - .replace("/notes/", "") - .split("#")[0]; - const currentURL = data.page.url; - - let backlinks = []; - let uniqueLinks = new Set(); - let counter = 1; - - for (const otherNote of notes) { - const noteContent = otherNote.template.frontMatter.content; - const backLinks = extractLinks(noteContent); - - if ( - !uniqueLinks.has(otherNote.url) && - backLinks.some( - (link) => - caselessCompare(link, currentFileSlug) || - currentURL == link.split("#")[0] - ) - ) { - let preview = noteContent.slice(0, 240); - backlinks.push({ - url: otherNote.url, - title: otherNote.data.title || otherNote.data.page.fileSlug, - preview, - id: counter++, - isHome: otherNote.data["dg-home"] || false, - }); - uniqueLinks.add(otherNote.url); +function getGraph(data) { + let nodes = {}; + let links = []; + let stemURLs = {}; + let homeAlias = "/"; + data.collections.note.forEach((v, idx) => { + let fpath = v.filePathStem.replace("/notes/", ""); + let parts = fpath.split("/"); + let group = "none"; + if (parts.length >= 3) { + group = parts[parts.length - 2]; } - } - return backlinks; + nodes[v.url] = { + id: idx, + title: v.data.title || v.fileSlug, + url: v.url, + group, + home: v.data["dg-home"] || false, + outBound: extractLinks(v.template.frontMatter.content), + neighbors: new Set(), + backLinks: new Set(), + }; + stemURLs[fpath] = v.url; + if (v.data["dg-home"]) { + homeAlias = v.url; + } + }); + Object.values(nodes).forEach((node) => { + let outBound = new Set(); + node.outBound.forEach((olink) => { + let link = (stemURLs[olink] || olink).split("#")[0]; + outBound.add(link); + }); + node.outBound = Array.from(outBound); + node.outBound.forEach((link) => { + let n = nodes[link]; + if (n) { + n.neighbors.add(node.url); + n.backLinks.add(node.url); + node.neighbors.add(n.url); + links.push({ source: node.id, target: n.id }); + } + }); + }); + Object.keys(nodes).map((k) => { + nodes[k].neighbors = Array.from(nodes[k].neighbors); + nodes[k].backLinks = Array.from(nodes[k].backLinks); + nodes[k].size = nodes[k].neighbors.length; + }); + return { + homeAlias, + nodes, + links, + }; } exports.wikiLinkRegex = wikiLinkRegex; exports.internalLinkRegex = internalLinkRegex; exports.extractLinks = extractLinks; -exports.getBacklinks = getBacklinks; +exports.getGraph = getGraph; diff --git a/src/site/_data/eleventyComputed.js b/src/site/_data/eleventyComputed.js new file mode 100644 index 0000000..d19e5e1 --- /dev/null +++ b/src/site/_data/eleventyComputed.js @@ -0,0 +1,5 @@ +const { getGraph } = require("../../helpers/linkUtils"); + +module.exports = { + graph: (data) => getGraph(data), +} \ No newline at end of file diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index ed0610d..f7185db 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -102,7 +102,7 @@ } function fetchGraphData() { - fetch('/graph').then(res => res.json()).then(data => { + fetch('/graph.json').then(res => res.json()).then(data => { window.graphData = data; Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 330, 330); }); diff --git a/src/site/_includes/components/sidebar.njk b/src/site/_includes/components/sidebar.njk index af033bd..82da48b 100644 --- a/src/site/_includes/components/sidebar.njk +++ b/src/site/_includes/components/sidebar.njk @@ -44,25 +44,41 @@ {%endif%} {%if settings.dgShowBacklinks === true %} + {%if settings.dgShowBacklinks === true %} + {%endif%} + {%endif%} diff --git a/src/site/graph.11tydata.js b/src/site/graph.11tydata.js deleted file mode 100644 index 973412d..0000000 --- a/src/site/graph.11tydata.js +++ /dev/null @@ -1,53 +0,0 @@ -const { extractLinks } = require("../helpers/linkUtils"); - -module.exports = { - eleventyComputed: { - graphData: (data) => { - let nodes = {}; - let links = []; - let stemURLs = {}; - data.collections.note.forEach((v, idx) => { - let fpath = v.filePathStem.replace("/notes/", ""); - let parts = fpath.split("/"); - let group = "none"; - if (parts.length >= 3) { - group = parts[parts.length - 2]; - } - nodes[v.url] = { - id: idx, - title: v.data.title || v.fileSlug, - url: v.url, - group, - home: v.data["dg-home"] || false, - outBound: extractLinks(v.template.frontMatter.content), - neighbors: new Set(), - }; - stemURLs[fpath] = v.url; - }); - Object.values(nodes).forEach((node) => { - let outBound = new Set(); - node.outBound.forEach((olink) => { - let link = (stemURLs[olink] || olink).split("#")[0]; - outBound.add(link); - }); - node.outBound = Array.from(outBound); - node.outBound.forEach((link) => { - let n = nodes[link]; - if (n) { - n.neighbors.add(node.url); - node.neighbors.add(n.url); - links.push({ source: node.id, target: n.id }); - } - }); - }); - Object.keys(nodes).map((k) => { - nodes[k].neighbors = Array.from(nodes[k].neighbors); - nodes[k].size = nodes[k].neighbors.length; - }); - return JSON.stringify({ - nodes, - links, - }); - }, - }, -}; diff --git a/src/site/graph.njk b/src/site/graph.njk index 9a24ca0..f40b5f9 100644 --- a/src/site/graph.njk +++ b/src/site/graph.njk @@ -1,5 +1,5 @@ --- -permalink: /graph +permalink: /graph.json eleventyExcludeFromCollections: true --- -{{ graphData | safe }} +{{ graph | jsonify | safe }} diff --git a/src/site/index.11tydata.js b/src/site/index.11tydata.js index 164a9e4..b38ca19 100644 --- a/src/site/index.11tydata.js +++ b/src/site/index.11tydata.js @@ -5,12 +5,10 @@ const markdownIt = require("markdown-it"); const md = markdownIt({ html: true, }).use(require("../helpers/utils").namedHeadingsFilter); -const { getBacklinks } = require("../helpers/linkUtils"); const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { eleventyComputed: { - backlinks: (data) => getBacklinks(data), settings: (data) => { const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0]; diff --git a/src/site/notes/notes.11tydata.js b/src/site/notes/notes.11tydata.js index f3b4049..c036a74 100644 --- a/src/site/notes/notes.11tydata.js +++ b/src/site/notes/notes.11tydata.js @@ -1,12 +1,10 @@ require("dotenv").config(); const settings = require("../../helpers/constants"); -const { getBacklinks } = require("../../helpers/linkUtils"); const allSettings = settings.ALL_NOTE_SETTINGS; module.exports = { eleventyComputed: { - backlinks: (data) => getBacklinks(data), settings: (data) => { const noteSettings = {}; allSettings.forEach((setting) => { From fb91fc90c7674af074b13f316e0d1d1338b39622 Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Thu, 2 Feb 2023 11:16:45 +0600 Subject: [PATCH 07/11] better zooming --- src/site/_includes/components/graphScript.njk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index f7185db..d6df231 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -93,7 +93,7 @@ .onNodeClick(node => { window.location = node.url; }); - if (delay != null) { + if (delay != null && graphData.nodes.length > 2) { setTimeout(() => { Graph.zoomToFit(5, 75); }, delay); @@ -104,7 +104,7 @@ function fetchGraphData() { fetch('/graph.json').then(res => res.json()).then(data => { window.graphData = data; - Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 330, 330); + Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 330, 330, 1); }); } @@ -119,7 +119,7 @@ Graph._destructor(); Graph = null; } - renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, null); + renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, 1); }) window.fullGraph = null; From 61195152bd747040801ea094090fed6b6c5b6a8d Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Thu, 2 Feb 2023 12:50:32 +0600 Subject: [PATCH 08/11] graph js fix --- src/site/graph.njk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/graph.njk b/src/site/graph.njk index f40b5f9..eb7abd0 100644 --- a/src/site/graph.njk +++ b/src/site/graph.njk @@ -2,4 +2,4 @@ permalink: /graph.json eleventyExcludeFromCollections: true --- -{{ graph | jsonify | safe }} +{{ graph | jsonify | safe }} \ No newline at end of file From e60c17cca294c77e1c24ebad7fe3ff540db926bd Mon Sep 17 00:00:00 2001 From: Ole Eskild Steensen Date: Wed, 1 Feb 2023 17:08:10 +0100 Subject: [PATCH 09/11] Fix theme issues --- .eleventy.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eleventy.js b/.eleventy.js index e62cb25..7770b1e 100644 --- a/.eleventy.js +++ b/.eleventy.js @@ -280,6 +280,7 @@ module.exports = function (eleventyConfig) { }); eleventyConfig.addPassthroughCopy("src/site/img"); + eleventyConfig.addPassthroughCopy("src/site/styles/_theme.*.css"); eleventyConfig.addPlugin(faviconPlugin, { destination: "dist" }); eleventyConfig.addPlugin(tocPlugin, { ul: true, @@ -304,7 +305,7 @@ module.exports = function (eleventyConfig) { output: "dist", data: `_data`, }, - templateFormats: ["njk", "md", "11ty.js", "css"], + templateFormats: ["njk", "md", "11ty.js"], htmlTemplateEngine: "njk", markdownTemplateEngine: "njk", passthroughFileCopy: true, From 556853f108044d347f132a8158aaf079408d2a78 Mon Sep 17 00:00:00 2001 From: Ole Eskild Steensen Date: Thu, 2 Feb 2023 10:07:05 +0100 Subject: [PATCH 10/11] Fixed styling issues in 404 page --- src/site/404.njk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/site/404.njk b/src/site/404.njk index 092d00c..3cf553f 100644 --- a/src/site/404.njk +++ b/src/site/404.njk @@ -6,7 +6,7 @@ {%-if meta.themeStyle%} - {{meta.themeStyle | safe}} + {% else %} {%endif%} From 062b6abb5bc8167bd515f2acef72902ab3d51dd2 Mon Sep 17 00:00:00 2001 From: Ole Eskild Steensen Date: Fri, 3 Feb 2023 16:23:30 +0100 Subject: [PATCH 11/11] Fix some styling issues, and use gardenEntry tag for home identification --- src/helpers/linkUtils.js | 4 +- src/site/_includes/components/graphScript.njk | 4 +- src/site/styles/digital-garden-base.scss | 93 +++++++++++++++++++ src/site/styles/obsidian-base.scss | 79 ---------------- 4 files changed, 97 insertions(+), 83 deletions(-) diff --git a/src/helpers/linkUtils.js b/src/helpers/linkUtils.js index d09e226..27bffa4 100644 --- a/src/helpers/linkUtils.js +++ b/src/helpers/linkUtils.js @@ -47,13 +47,13 @@ function getGraph(data) { title: v.data.title || v.fileSlug, url: v.url, group, - home: v.data["dg-home"] || false, + home: v.data["dg-home"] || (v.data.tags && v.data.tags.indexOf("gardenEntry") > -1)|| false, outBound: extractLinks(v.template.frontMatter.content), neighbors: new Set(), backLinks: new Set(), }; stemURLs[fpath] = v.url; - if (v.data["dg-home"]) { + if (v.data["dg-home"] || (v.data.tags && v.data.tags.indexOf("gardenEntry") > -1)) { homeAlias = v.url; } }); diff --git a/src/site/_includes/components/graphScript.njk b/src/site/_includes/components/graphScript.njk index d6df231..4272bdf 100644 --- a/src/site/_includes/components/graphScript.njk +++ b/src/site/_includes/components/graphScript.njk @@ -104,7 +104,7 @@ function fetchGraphData() { fetch('/graph.json').then(res => res.json()).then(data => { window.graphData = data; - Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 330, 330, 1); + Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 320, 320, 1); }); } @@ -151,4 +151,4 @@ renderFullGraph(); } }); - \ No newline at end of file + diff --git a/src/site/styles/digital-garden-base.scss b/src/site/styles/digital-garden-base.scss index 252573c..72a23e9 100644 --- a/src/site/styles/digital-garden-base.scss +++ b/src/site/styles/digital-garden-base.scss @@ -520,3 +520,96 @@ ul.task-list { .callout-fold .lucide { transition: transform 100ms ease-in-out; } + + + + +// Graph Controls +.graph-title-container { + position: relative; +} + +#full-graph { + position: fixed; + top: 50%; + left: 50%; + height: 60vh; + width: 60vw; + min-height: 400px; + min-width: 400px; + transform: translate(-50%, -50%); + z-index: 9999; + display: none; + background-color: var(--background-secondary); + + #full-graph-close { + position: absolute; + top: 10px; + right: 10px; + cursor: pointer; + z-index: 9; + } +} + +#graph-full-btn { + margin-right: 10px; +} + +#full-graph.show { + display: block; +} + +#graph-controls { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 5px; + position: absolute; + top: 115%; + cursor: pointer; + right: 0px; + left: 10px; + color: var(--text-accent); + z-index: 9; + + .depth-control { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 7px; + + .slider { + datalist { + display: flex; + flex-direction: row; + justify-content: space-between; + font-size: 0.6rem; + // padding: 2px 0px; + // width: 200px; + } + + option { + padding: 0; + } + } + + #depth-display { + background-color: var(--text-accent); + color: white; + width: 1rem; + height: 1rem; + font-size: 0.8rem; + display: flex; + justify-content: center; + align-items: center; + margin-top: 0.3rem; + border-radius: 50%; + } + } +} + +input[type=range]::-webkit-slider-thumb { + -webkit-appearance: none; + margin-top: -10px; +} diff --git a/src/site/styles/obsidian-base.scss b/src/site/styles/obsidian-base.scss index d743e80..563409a 100644 --- a/src/site/styles/obsidian-base.scss +++ b/src/site/styles/obsidian-base.scss @@ -10634,82 +10634,3 @@ body { width: 100%; } - // Graph Controls - .graph-title-container { - position: relative; -} - -#full-graph { - position: fixed; - top: 50%; - left: 50%; - height: 60vh; - width: 60vw; - min-height: 400px; - min-width: 400px; - transform: translate(-50%, -50%); - z-index: 9999; - display: none; - background-color: var(--background-secondary); - - #full-graph-close { - position: absolute; - top: 10px; - right: 10px; - cursor: pointer; - z-index: 9; - } -} - -#full-graph.show { - display: block; -} - -#graph-controls { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - gap: 5px; - position: absolute; - top: 115%; - cursor: pointer; - right: 0px; - left: 10px; - color: var(--text-accent); - z-index: 9; - - .depth-control { - display: flex; - flex-direction: row; - align-items: flex-start; - gap: 7px; - - .slider { - datalist { - display: flex; - flex-direction: row; - justify-content: space-between; - font-size: 0.6rem; - // padding: 2px 0px; - // width: 200px; - } - - option { - padding: 0; - } - } - - #depth-display { - background-color: var(--text-accent); - color: white; - width: 1rem; - height: 1rem; - font-size: 0.8rem; - display: flex; - justify-content: center; - align-items: center; - margin-top: 0.3rem; - } - } -} \ No newline at end of file