From 6edc732dcc537c219e7b43f0437165319ea1ffec Mon Sep 17 00:00:00 2001 From: Utsob Roy Date: Wed, 1 Feb 2023 10:52:37 +0600 Subject: [PATCH] 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