mirror of
https://github.com/tcsenpai/obsidiangarden_netlify.git
synced 2025-06-06 20:55:21 +00:00
Merge branch 'uroybd-graph-revamp'
This commit is contained in:
commit
a98720e8e3
@ -1,108 +1,92 @@
|
|||||||
const wikilink = /\[\[(.*?\|.*?)\]\]/g
|
const wikiLinkRegex = /\[\[(.*?\|.*?)\]\]/g;
|
||||||
const internalLinkRegex = /href="\/(.*?)"/g;
|
const internalLinkRegex = /href="\/(.*?)"/g;
|
||||||
|
|
||||||
|
|
||||||
function caselessCompare(a, b) {
|
function caselessCompare(a, b) {
|
||||||
return a.toLowerCase() === b.toLowerCase();
|
return a.toLowerCase() === b.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractLinks(content) {
|
function extractLinks(content) {
|
||||||
return[...(content.match(wikilink) || []).map(link => (
|
return [
|
||||||
link.slice(2, -2)
|
...(content.match(wikiLinkRegex) || []).map(
|
||||||
.split("|")[0]
|
(link) =>
|
||||||
.replace(/.(md|markdown)\s?$/i, "")
|
link
|
||||||
.replace("\\", "")
|
.slice(2, -2)
|
||||||
.trim()
|
.split("|")[0]
|
||||||
)), ...(content.match(internalLinkRegex) || []).map(
|
.replace(/.(md|markdown)\s?$/i, "")
|
||||||
(link) =>
|
.replace("\\", "")
|
||||||
link
|
.trim()
|
||||||
.slice(6, -1)
|
.split("#")[0]
|
||||||
.split("|")[0]
|
),
|
||||||
.replace(/.(md|markdown)\s?$/i, "")
|
...(content.match(internalLinkRegex) || []).map(
|
||||||
.replace("\\", "")
|
(link) =>
|
||||||
.trim()
|
link
|
||||||
)];
|
.slice(6, -1)
|
||||||
|
.split("|")[0]
|
||||||
|
.replace(/.(md|markdown)\s?$/i, "")
|
||||||
|
.replace("\\", "")
|
||||||
|
.trim()
|
||||||
|
.split("#")[0]
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBacklinks(data) {
|
function getGraph(data) {
|
||||||
const notes = data.collections.note;
|
let nodes = {};
|
||||||
if (!notes) {
|
let links = [];
|
||||||
return [];
|
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];
|
||||||
}
|
}
|
||||||
const currentFileSlug = data.page.filePathStem.replace('/notes/', '');
|
nodes[v.url] = {
|
||||||
const currentURL = data.page.url;
|
id: idx,
|
||||||
|
title: v.data.title || v.fileSlug,
|
||||||
let backlinks = [];
|
url: v.url,
|
||||||
let uniqueLinks = new Set();
|
group,
|
||||||
let counter = 1;
|
home: v.data["dg-home"] || (v.data.tags && v.data.tags.indexOf("gardenEntry") > -1)|| false,
|
||||||
|
outBound: extractLinks(v.template.frontMatter.content),
|
||||||
for (const otherNote of notes) {
|
neighbors: new Set(),
|
||||||
const noteContent = otherNote.template.frontMatter.content;
|
backLinks: new Set(),
|
||||||
const backLinks = extractLinks(noteContent);
|
};
|
||||||
|
stemURLs[fpath] = v.url;
|
||||||
if (!uniqueLinks.has(otherNote.url) && backLinks.some(link => caselessCompare(link, currentFileSlug) ||
|
if (v.data["dg-home"] || (v.data.tags && v.data.tags.indexOf("gardenEntry") > -1)) {
|
||||||
currentURL == link.split("#")[0])) {
|
homeAlias = v.url;
|
||||||
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;
|
});
|
||||||
|
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,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOutboundLinks(data, isHome=false){
|
exports.wikiLinkRegex = wikiLinkRegex;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.wikilink = wikilink;
|
|
||||||
exports.internalLinkRegex = internalLinkRegex;
|
exports.internalLinkRegex = internalLinkRegex;
|
||||||
exports.getBacklinks = getBacklinks;
|
exports.extractLinks = extractLinks;
|
||||||
exports.getOutboundLinks = getOutboundLinks;
|
exports.getGraph = getGraph;
|
||||||
exports.caselessCompare = caselessCompare;
|
|
||||||
exports.extractLinks = extractLinks;
|
|
||||||
|
5
src/site/_data/eleventyComputed.js
Normal file
5
src/site/_data/eleventyComputed.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const { getGraph } = require("../../helpers/linkUtils");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
graph: (data) => getGraph(data),
|
||||||
|
}
|
@ -1,113 +1,86 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
const getCssVar = (variable) => getComputedStyle(document.body).getPropertyValue(variable);
|
const getCssVar = (variable) => getComputedStyle(document.body).getPropertyValue(variable);
|
||||||
|
|
||||||
function htmlDecode(input) {
|
function htmlDecode(input) {
|
||||||
var doc = new DOMParser().parseFromString(input, "text/html");
|
var doc = new DOMParser().parseFromString(input, "text/html");
|
||||||
return doc.documentElement.textContent;
|
return doc.documentElement.textContent;
|
||||||
}
|
}
|
||||||
|
window.graphData = null;
|
||||||
|
window.maxGraphDepth = 1;
|
||||||
|
|
||||||
const backLinks = [
|
function getNextLevelNeighbours(existing, remaining) {
|
||||||
|
const keys = Object.values(existing).map((n) => n.neighbors).flat();
|
||||||
|
const n_remaining = Object.keys(remaining).reduce((acc, key) => {
|
||||||
|
if (keys.indexOf(key) != -1) {
|
||||||
|
existing[key] = remaining[key];
|
||||||
|
} else {
|
||||||
|
acc[key] = remaining[key];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return existing, n_remaining;
|
||||||
|
}
|
||||||
|
|
||||||
{%- for backlink in backlinks -%}
|
function filterToDepth(data) {
|
||||||
{
|
let remaining = JSON.parse(JSON.stringify(data.nodes));
|
||||||
id: {{backlink.id}},
|
let currentLink = window.location.pathname;
|
||||||
title: "{{backlink.title | safe}}",
|
let currentNode = remaining[currentLink] || Object.values(remaining).find((v) => v.home);
|
||||||
url: "{{backlink.url}}"
|
delete remaining[currentNode.url];
|
||||||
},
|
if (!currentNode.home) {
|
||||||
{%- endfor -%}
|
let home = Object.values(remaining).find((v) => v.home);
|
||||||
];
|
delete remaining[home.url];
|
||||||
|
|
||||||
const outbound = [
|
|
||||||
|
|
||||||
{%- for out in outbound -%}
|
|
||||||
{
|
|
||||||
id: {{out.id}},
|
|
||||||
title: "{{out.title | safe}}",
|
|
||||||
url: "{{out.url}}"
|
|
||||||
},
|
|
||||||
{%- endfor -%}
|
|
||||||
].map(x => {
|
|
||||||
x.id += backLinks.length;
|
|
||||||
return x;
|
|
||||||
});
|
|
||||||
|
|
||||||
const outboundDuplicatesRemoved = outbound.filter(x => !backLinks.find(b => b.url === x.url));
|
|
||||||
|
|
||||||
const title = "{{page.fileSlug}}" || "Home"
|
|
||||||
const currentNode = {
|
|
||||||
title,
|
|
||||||
id: 0,
|
|
||||||
url: "{{page.url}}"
|
|
||||||
};
|
|
||||||
|
|
||||||
const gData = {
|
|
||||||
nodes: [
|
|
||||||
currentNode,
|
|
||||||
...backLinks,
|
|
||||||
...outboundDuplicatesRemoved
|
|
||||||
],
|
|
||||||
links: [
|
|
||||||
...backLinks.map(backlink => ({source: backlink.id, target: 0})),
|
|
||||||
...outboundDuplicatesRemoved.map(outlink => ({source: 0, target: outlink.id}))
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
gData
|
|
||||||
.links
|
|
||||||
.forEach(link => {
|
|
||||||
const a = gData
|
|
||||||
.nodes
|
|
||||||
.find(x => x.id === link.source);
|
|
||||||
const b = gData
|
|
||||||
.nodes
|
|
||||||
.find(x => x.id === link.target);
|
|
||||||
if (a && b) {
|
|
||||||
|
|
||||||
!a.neighbors && (a.neighbors = []);
|
|
||||||
!b.neighbors && (b.neighbors = []);
|
|
||||||
a
|
|
||||||
.neighbors
|
|
||||||
.push(b);
|
|
||||||
b
|
|
||||||
.neighbors
|
|
||||||
.push(a);
|
|
||||||
|
|
||||||
!a.links && (a.links = []);
|
|
||||||
!b.links && (b.links = []);
|
|
||||||
a
|
|
||||||
.links
|
|
||||||
.push(link);
|
|
||||||
b
|
|
||||||
.links
|
|
||||||
.push(link);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let Graph;
|
|
||||||
function renderGraph(width, height) {
|
|
||||||
|
|
||||||
if (Graph) {
|
|
||||||
Graph
|
|
||||||
.width(width)
|
|
||||||
.height(height);
|
|
||||||
|
|
||||||
Graph.zoomToFit()
|
|
||||||
Graph.zoom(3)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
currentNode.current = true;
|
||||||
|
let existing = {};
|
||||||
|
existing[currentNode.url] = currentNode;
|
||||||
|
for (let i = 0; i < window.maxGraphDepth; i++) {
|
||||||
|
existing, remaining = getNextLevelNeighbours(existing, remaining);
|
||||||
|
}
|
||||||
|
nodes = Object.values(existing);
|
||||||
|
if (!currentNode.home) {
|
||||||
|
nodes = nodes.filter(n => !n.home);
|
||||||
|
}
|
||||||
|
let ids = nodes.map((n) => n.id);
|
||||||
|
let graphData = {
|
||||||
|
nodes,
|
||||||
|
links: data.links.filter((con) => ids.indexOf(con.target) > -1 && ids.indexOf(con.source) > -1),
|
||||||
|
}
|
||||||
|
return graphData;
|
||||||
|
}
|
||||||
|
|
||||||
Graph = ForceGraph()(document.getElementById('link-graph'))
|
var Graph;
|
||||||
|
|
||||||
|
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)
|
.width(width)
|
||||||
.height(height)
|
.height(height)
|
||||||
|
.linkDirectionalArrowLength(2)
|
||||||
|
.linkDirectionalArrowRelPos(0.5)
|
||||||
|
.linkColor(() => getCssVar("--text-muted") || getCssVar("--text-normal"))
|
||||||
.nodeCanvasObject((node, ctx) => {
|
.nodeCanvasObject((node, ctx) => {
|
||||||
const numberOfLinks = (node.links && node.links.length) || 2;
|
const color = getCssVar("--text-accent");
|
||||||
const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2;
|
const numberOfNeighbours = (node.neighbors && node.neighbors.length) || 2;
|
||||||
const nodeR = Math.min(7, Math.max((numberOfLinks + numberOfNeighbours) / 2, 2));
|
const nodeR = Math.min(7, Math.max(numberOfNeighbours / 2, 2));
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false);
|
ctx.arc(node.x, node.y, nodeR, 0, 2 * Math.PI, false);
|
||||||
ctx.fillStyle = getCssVar("--text-accent");
|
ctx.fillStyle = color;
|
||||||
ctx.fill();
|
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 label = htmlDecode(node.title)
|
||||||
const fontSize = 3.5;
|
const fontSize = 3.5;
|
||||||
@ -117,17 +90,65 @@
|
|||||||
ctx.textBaseline = 'top';
|
ctx.textBaseline = 'top';
|
||||||
ctx.fillText(label, node.x, node.y + nodeR + 2);
|
ctx.fillText(label, node.x, node.y + nodeR + 2);
|
||||||
})
|
})
|
||||||
.linkColor(() => getCssVar("--text-muted") || getCssVar("--text-normal"))
|
|
||||||
.graphData(gData)
|
|
||||||
.onNodeClick(node => {
|
.onNodeClick(node => {
|
||||||
window.location = node.url;
|
window.location = node.url;
|
||||||
});
|
});
|
||||||
|
if (delay != null && graphData.nodes.length > 2) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
Graph.zoomToFit();
|
Graph.zoomToFit(5, 75);
|
||||||
Graph.zoom(3);
|
}, delay);
|
||||||
}, 10);
|
}
|
||||||
|
return Graph;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderGraph(320,320);
|
function fetchGraphData() {
|
||||||
</script>
|
fetch('/graph.json').then(res => res.json()).then(data => {
|
||||||
|
window.graphData = data;
|
||||||
|
Graph = renderGraph(filterToDepth(JSON.parse(JSON.stringify(data))), "link-graph", 320, 320, 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchGraphData();
|
||||||
|
window.document.getElementById('graph-depth').value = window.maxGraphDepth;
|
||||||
|
window.document.getElementById('depth-display').innerText = window.maxGraphDepth;
|
||||||
|
window.document.getElementById('graph-depth').addEventListener('input', (evt) => {
|
||||||
|
|
||||||
|
window.maxGraphDepth = evt.target.value;
|
||||||
|
window.document.getElementById('depth-display').innerText = window.maxGraphDepth;
|
||||||
|
if (Graph != null) {
|
||||||
|
Graph._destructor();
|
||||||
|
Graph = null;
|
||||||
|
}
|
||||||
|
renderGraph(filterToDepth(JSON.parse(JSON.stringify(window.graphData))), "link-graph", 330, 330, 1);
|
||||||
|
})
|
||||||
|
|
||||||
|
window.fullGraph = null;
|
||||||
|
function renderFullGraph() {
|
||||||
|
if (!window.fullGraph) {
|
||||||
|
const graphData = {
|
||||||
|
links: JSON.parse(JSON.stringify(window.graphData.links)),
|
||||||
|
nodes: [...Object.values(window.graphData.nodes)]
|
||||||
|
}
|
||||||
|
|
||||||
|
let g = document.createElement('div');
|
||||||
|
g.id = 'full-graph';
|
||||||
|
g.classList.add('show');
|
||||||
|
document.body.appendChild(g);
|
||||||
|
g.innerHTML = '<i class="fa fa-times" id="full-graph-close" aria-hidden="true"></i><div id="full-graph-container"></div>';
|
||||||
|
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();
|
||||||
|
window.fullGraph = null;
|
||||||
|
document.getElementById('full-graph').remove()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('graph-full-btn').addEventListener('click', (evt) => {
|
||||||
|
if (!fullGraph) {
|
||||||
|
renderFullGraph();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism-okaidia.min.css" integrity="sha512-mIs9kKbaw6JZFfSuo+MovjU+Ntggfoj8RwAmJbVXQ5mkAX5LlgETQEweFPI18humSPHymTb5iikEOKWF7I8ncQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism-okaidia.min.css" integrity="sha512-mIs9kKbaw6JZFfSuo+MovjU+Ntggfoj8RwAmJbVXQ5mkAX5LlgETQEweFPI18humSPHymTb5iikEOKWF7I8ncQ==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
|
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
|
||||||
<link href="/styles/digital-garden-base.css" rel="stylesheet">
|
<link href="/styles/digital-garden-base.css" rel="stylesheet">
|
||||||
|
@ -6,6 +6,21 @@
|
|||||||
<div class="graph">
|
<div class="graph">
|
||||||
<div class="graph-title-container">
|
<div class="graph-title-container">
|
||||||
<div class="graph-title">Connected Pages</div>
|
<div class="graph-title">Connected Pages</div>
|
||||||
|
<div id="graph-controls">
|
||||||
|
<div class="depth-control">
|
||||||
|
<label for="graph-depth">Depth</label>
|
||||||
|
<div class="slider">
|
||||||
|
<input name="graph-depth" list="depthmarkers" type="range" step="1" min="1" max="3" id="graph-depth"/>
|
||||||
|
<datalist id="depthmarkers">
|
||||||
|
<option value="1" label="1"></option>
|
||||||
|
<option value="2" label="2"></option>
|
||||||
|
<option value="3" label="3"></option>
|
||||||
|
</datalist>
|
||||||
|
</div>
|
||||||
|
<span id="depth-display"></span>
|
||||||
|
</div>
|
||||||
|
<i class="fa fa-arrows-alt" id="graph-full-btn" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="link-graph"></div>
|
<div id="link-graph"></div>
|
||||||
</div>
|
</div>
|
||||||
@ -29,25 +44,41 @@
|
|||||||
{%endif%}
|
{%endif%}
|
||||||
|
|
||||||
{%if settings.dgShowBacklinks === true %}
|
{%if settings.dgShowBacklinks === true %}
|
||||||
|
{%if settings.dgShowBacklinks === true %}
|
||||||
<div class="backlinks">
|
<div class="backlinks">
|
||||||
<div class="backlink-title" style="margin: 4px 0 !important;">Pages mentioning this page</div>
|
<div class="backlink-title" style="margin: 4px 0 !important;">Pages mentioning this page</div>
|
||||||
<div class="backlink-list">
|
<div class="backlink-list">
|
||||||
|
{%- if page.url == "/" -%}
|
||||||
{%- if backlinks.length === 0 -%}
|
{%- if graph.nodes[graph.homeAlias].backLinks.length === 0 -%}
|
||||||
|
<div class="backlink-card">
|
||||||
|
<span class="no-backlinks-message">No other pages mentions this page</span>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- for backlink in graph.nodes[graph.homeAlias].backLinks -%}
|
||||||
|
{%- if graph.nodes[backlink].url != graph.homeAlias -%}
|
||||||
<div class="backlink-card">
|
<div class="backlink-card">
|
||||||
<span class="no-backlinks-message">No other pages mentions this page</span>
|
<i class="fa fa-link"></i> <a href="{{graph.nodes[backlink].url}}">{{graph.nodes[backlink].title}}</a>
|
||||||
</div>
|
|
||||||
{%- endif -%}
|
|
||||||
|
|
||||||
{%- for backlink in backlinks -%}
|
|
||||||
<div class="backlink-card">
|
|
||||||
<i class="fa fa-link"></i> <a href="{{backlink.url}}">{{backlink.title}}</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
{%- else -%}
|
||||||
|
{%- if graph.nodes[page.url].backLinks.length === 0 -%}
|
||||||
|
<div class="backlink-card">
|
||||||
|
<span class="no-backlinks-message">No other pages mentions this page</span>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- for backlink in graph.nodes[page.url].backLinks -%}
|
||||||
|
{%- if graph.nodes[backlink].url != page.url -%}
|
||||||
|
<div class="backlink-card">
|
||||||
|
<i class="fa fa-link"></i> <a href="{{graph.nodes[backlink].url}}">{{graph.nodes[backlink].title}}</a>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%endif%}
|
{%endif%}
|
||||||
|
{%endif%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
5
src/site/graph.njk
Normal file
5
src/site/graph.njk
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
permalink: /graph.json
|
||||||
|
eleventyExcludeFromCollections: true
|
||||||
|
---
|
||||||
|
{{ graph | jsonify | safe }}
|
@ -1,55 +1,59 @@
|
|||||||
|
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
const settings = require("../helpers/constants");
|
const settings = require("../helpers/constants");
|
||||||
|
|
||||||
const markdownIt = require("markdown-it");
|
const markdownIt = require("markdown-it");
|
||||||
const { getBacklinks, getOutboundLinks } = require("../helpers/linkUtils");
|
|
||||||
const md = markdownIt({
|
const md = markdownIt({
|
||||||
html: true,
|
html: true,
|
||||||
}).use(require("../helpers/utils").namedHeadingsFilter);
|
}).use(require("../helpers/utils").namedHeadingsFilter);
|
||||||
|
|
||||||
const allSettings = settings.ALL_NOTE_SETTINGS;
|
const allSettings = settings.ALL_NOTE_SETTINGS;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
eleventyComputed: {
|
eleventyComputed: {
|
||||||
backlinks: (data) => getBacklinks(data),
|
settings: (data) => {
|
||||||
outbound: (data) => getOutboundLinks(data, true),
|
const currentnote =
|
||||||
settings: (data) => {
|
data.collections.gardenEntry && data.collections.gardenEntry[0];
|
||||||
const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0];
|
if (currentnote && currentnote.data) {
|
||||||
if (currentnote && currentnote.data) {
|
const noteSettings = {};
|
||||||
const noteSettings = {};
|
allSettings.forEach((setting) => {
|
||||||
allSettings.forEach(setting => {
|
let noteSetting = currentnote.data[setting];
|
||||||
let noteSetting = currentnote.data[setting];
|
let globalSetting = process.env[setting];
|
||||||
let globalSetting = process.env[setting];
|
|
||||||
|
|
||||||
let settingValue = (noteSetting || (globalSetting === 'true' && noteSetting !== false));
|
let settingValue =
|
||||||
noteSettings[setting] = settingValue;
|
noteSetting || (globalSetting === "true" && noteSetting !== false);
|
||||||
});
|
noteSettings[setting] = settingValue;
|
||||||
return noteSettings;
|
});
|
||||||
|
return noteSettings;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
noteTitle: (data) => {
|
noteTitle: (data) => {
|
||||||
const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0];
|
const currentnote =
|
||||||
if (currentnote && currentnote.data) {
|
data.collections.gardenEntry && data.collections.gardenEntry[0];
|
||||||
return currentnote.data.title || currentnote.data.page.fileSlug;
|
if (currentnote && currentnote.data) {
|
||||||
}
|
return currentnote.data.title || currentnote.data.page.fileSlug;
|
||||||
return "";
|
}
|
||||||
},
|
return "";
|
||||||
tags: (data) => {
|
},
|
||||||
const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0];
|
tags: (data) => {
|
||||||
if (currentnote && currentnote.data) {
|
const currentnote =
|
||||||
return currentnote.data.tags;
|
data.collections.gardenEntry && data.collections.gardenEntry[0];
|
||||||
}
|
if (currentnote && currentnote.data) {
|
||||||
return [];
|
return currentnote.data.tags;
|
||||||
},
|
}
|
||||||
content: (data) => {
|
return [];
|
||||||
const currentnote = data.collections.gardenEntry && data.collections.gardenEntry[0];
|
},
|
||||||
if (currentnote && currentnote.template && currentnote.template.frontMatter && currentnote.template.frontMatter.content) {
|
content: (data) => {
|
||||||
return md.render(currentnote.template.frontMatter.content);
|
const currentnote =
|
||||||
}
|
data.collections.gardenEntry && data.collections.gardenEntry[0];
|
||||||
return "";
|
if (
|
||||||
}
|
currentnote &&
|
||||||
}
|
currentnote.template &&
|
||||||
}
|
currentnote.template.frontMatter &&
|
||||||
|
currentnote.template.frontMatter.content
|
||||||
|
) {
|
||||||
|
return md.render(currentnote.template.frontMatter.content);
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
|
|
||||||
require("dotenv").config();
|
require("dotenv").config();
|
||||||
const settings = require("../../helpers/constants");
|
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 = {
|
module.exports = {
|
||||||
eleventyComputed: {
|
eleventyComputed: {
|
||||||
backlinks: (data) => getBacklinks(data),
|
settings: (data) => {
|
||||||
outbound: (data) => getOutboundLinks(data),
|
const noteSettings = {};
|
||||||
settings: (data) => {
|
allSettings.forEach((setting) => {
|
||||||
const noteSettings = {};
|
let noteSetting = data[setting];
|
||||||
allSettings.forEach(setting => {
|
let globalSetting = process.env[setting];
|
||||||
let noteSetting = data[setting];
|
|
||||||
let globalSetting = process.env[setting];
|
|
||||||
|
|
||||||
let settingValue = (noteSetting || (globalSetting === 'true' && noteSetting !== false));
|
let settingValue =
|
||||||
noteSettings[setting] = settingValue;
|
noteSetting || (globalSetting === "true" && noteSetting !== false);
|
||||||
});
|
noteSettings[setting] = settingValue;
|
||||||
return noteSettings;
|
});
|
||||||
}
|
return noteSettings;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
@ -520,3 +520,96 @@ ul.task-list {
|
|||||||
.callout-fold .lucide {
|
.callout-fold .lucide {
|
||||||
transition: transform 100ms ease-in-out;
|
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;
|
||||||
|
}
|
||||||
|
@ -7943,6 +7943,8 @@ body {
|
|||||||
.graph-control-section.mod-display .setting-item:not(.mod-slider):last-child .setting-item-info {
|
.graph-control-section.mod-display .setting-item:not(.mod-slider):last-child .setting-item-info {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.workspace-leaf-content[data-type='outline'] .view-content {
|
.workspace-leaf-content[data-type='outline'] .view-content {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
@ -10632,4 +10634,3 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user