From bf3e060a13f86b1ca1c71f3f1bdab6c54261bcd8 Mon Sep 17 00:00:00 2001 From: Jet Hughes Date: Sat, 7 May 2022 21:44:30 +1200 Subject: [PATCH] updated base quartz --- .github/FUNDING.yml | 2 +- .github/ISSUE_TEMPLATE/bug_report.md | 64 +- .github/ISSUE_TEMPLATE/feature_request.md | 40 +- Makefile | 38 +- assets/js/darkmode.js | 58 +- assets/js/graph.js | 503 +++++---- assets/js/popover.js | 93 +- assets/js/router.js | 12 + assets/js/search.js | 509 +++++---- assets/styles/base.scss | 1162 ++++++++++---------- assets/styles/darkmode.scss | 86 +- data/graphConfig.yaml | 10 +- layouts/404.html | 32 +- layouts/_default/_markup/render-image.html | 16 +- layouts/_default/_markup/render-link.html | 32 +- layouts/_default/baseof.html | 18 +- layouts/_default/section.html | 49 +- layouts/_default/single.html | 65 +- layouts/_default/taxonomy.html | 67 +- layouts/_default/term.html | 49 +- layouts/index.html | 46 +- layouts/partials/backlinks.html | 54 +- layouts/partials/contact.html | 28 +- layouts/partials/darkmode.html | 28 +- layouts/partials/footer.html | 20 +- layouts/partials/graph.html | 43 +- layouts/partials/head.html | 156 ++- layouts/partials/katex.html | 21 +- layouts/partials/page-list.html | 30 +- layouts/partials/popover.html | 7 - layouts/partials/search.html | 20 +- layouts/partials/textprocessing.html | 116 +- layouts/partials/toc.html | 16 +- 33 files changed, 1842 insertions(+), 1648 deletions(-) create mode 100644 assets/js/router.js delete mode 100644 layouts/partials/popover.html diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 2824084fb..40b2d4a3a 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -github: [jackyzha0] +github: [jackyzha0] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5c8616f42..189648d9c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,32 +1,32 @@ ---- -name: Bug report -about: Something about Quartz isn't working the way you expect -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - -**Additional context** -Add any other context about the problem here. +--- +name: Bug report +about: Something about Quartz isn't working the way you expect +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 80e19eff6..2c9c226d9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,20 +1,20 @@ ---- -name: Feature request -about: Suggest an idea or improvement for Quartz -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. +--- +name: Feature request +about: Suggest an idea or improvement for Quartz +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/Makefile b/Makefile index 4310e9478..0d29bdacf 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,20 @@ -.DEFAULT_GOAL := serve - -help: ## Show all Makefile targets - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -update: ## Update Quartz to the latest version on Github - @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git) - git fetch upstream - git log --oneline --decorate --graph ..upstream/hugo - git checkout -p upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data - -update-force: ## Forcefully pull all changes and don't ask to patch - @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git) - git fetch upstream - git checkout upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data - -serve: ## Serve Quartz locally - hugo-obsidian -input=content -output=assets/indices -index -root=. && hugo server --enableGitInfo +.DEFAULT_GOAL := serve + +help: ## Show all Makefile targets + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +update: ## Update Quartz to the latest version on Github + go install github.com/jackyzha0/hugo-obsidian@latest + @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git) + git fetch upstream + git log --oneline --decorate --graph ..upstream/hugo + git checkout -p upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data + +update-force: ## Forcefully pull all changes and don't ask to patch + go install github.com/jackyzha0/hugo-obsidian@latest + @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git) + git fetch upstream + git checkout upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data + +serve: ## Serve Quartz locally + hugo-obsidian -input=content -output=assets/indices -index -root=. && hugo server --enableGitInfo diff --git a/assets/js/darkmode.js b/assets/js/darkmode.js index a9f170f30..d95a281ac 100644 --- a/assets/js/darkmode.js +++ b/assets/js/darkmode.js @@ -1,29 +1,29 @@ -const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark' -const currentTheme = localStorage.getItem('theme') ?? userPref - -if (currentTheme) { - document.documentElement.setAttribute('saved-theme', currentTheme); -} - -const switchTheme = (e) => { - if (e.target.checked) { - document.documentElement.setAttribute('saved-theme', 'dark') - localStorage.setItem('theme', 'dark') - } - else { - document.documentElement.setAttribute('saved-theme', 'light') - localStorage.setItem('theme', 'light') - } -} - -window.addEventListener('DOMContentLoaded', () => { - // Darkmode toggle - const toggleSwitch = document.querySelector('#darkmode-toggle') - - // listen for toggle - toggleSwitch.addEventListener('change', switchTheme, false) - - if (currentTheme === 'dark') { - toggleSwitch.checked = true - } -}) +const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark' +const currentTheme = localStorage.getItem('theme') ?? userPref + +if (currentTheme) { + document.documentElement.setAttribute('saved-theme', currentTheme); +} + +const switchTheme = (e) => { + if (e.target.checked) { + document.documentElement.setAttribute('saved-theme', 'dark') + localStorage.setItem('theme', 'dark') + } + else { + document.documentElement.setAttribute('saved-theme', 'light') + localStorage.setItem('theme', 'light') + } +} + +window.addEventListener('DOMContentLoaded', () => { + // Darkmode toggle + const toggleSwitch = document.querySelector('#darkmode-toggle') + + // listen for toggle + toggleSwitch.addEventListener('change', switchTheme, false) + + if (currentTheme === 'dark') { + toggleSwitch.checked = true + } +}) diff --git a/assets/js/graph.js b/assets/js/graph.js index e5b42cd43..f71e44d37 100644 --- a/assets/js/graph.js +++ b/assets/js/graph.js @@ -1,220 +1,283 @@ -async function drawGraph(url, baseUrl, pathColors, depth, enableDrag, enableLegend, enableZoom) { - const { index, links, content } = await fetchData - const curPage = url.replace(baseUrl, "") - - const parseIdsFromLinks = (links) => [...(new Set(links.flatMap(link => ([link.source, link.target]))))] - - const neighbours = new Set() - const wl = [curPage || "/", "__SENTINEL"] - if (depth >= 0) { - while (depth >= 0 && wl.length > 0) { - // compute neighbours - const cur = wl.shift() - if (cur === "__SENTINEL") { - depth-- - wl.push("__SENTINEL") - } else { - neighbours.add(cur) - const outgoing = index.links[cur] || [] - const incoming = index.backlinks[cur] || [] - wl.push(...outgoing.map(l => l.target), ...incoming.map(l => l.source)) - } - } - } else { - parseIdsFromLinks(links).forEach(id => neighbours.add(id)) - } - - const data = { - nodes: [...neighbours].map(id => ({ id })), - links: links.filter(l => neighbours.has(l.source) && neighbours.has(l.target)), - } - - const color = (d) => { - if (d.id === curPage || (d.id === "/" && curPage === "")) { - return "var(--g-node-active)" - } - - for (const pathColor of pathColors) { - const path = Object.keys(pathColor)[0] - const colour = pathColor[path] - if (d.id.startsWith(path)) { - return colour - } - } - - return "var(--g-node)" - } - - const drag = simulation => { - function dragstarted(event, d) { - if (!event.active) simulation.alphaTarget(1).restart(); - d.fx = d.x; - d.fy = d.y; - } - - function dragged(event, d) { - d.fx = event.x; - d.fy = event.y; - } - - function dragended(event, d) { - if (!event.active) simulation.alphaTarget(0); - d.fx = null; - d.fy = null; - } - - const noop = () => { } - return d3.drag() - .on("start", enableDrag ? dragstarted : noop) - .on("drag", enableDrag ? dragged : noop) - .on("end", enableDrag ? dragended : noop); - } - - const height = 250 - const width = document.getElementById("graph-container").offsetWidth - - const simulation = d3.forceSimulation(data.nodes) - .force("charge", d3.forceManyBody().strength(-30)) - .force("link", d3.forceLink(data.links).id(d => d.id)) - .force("center", d3.forceCenter()); - - const svg = d3.select('#graph-container') - .append('svg') - .attr('width', width) - .attr('height', height) - .attr("viewBox", [-width / 2, -height / 2, width, height]); - - if (enableLegend) { - const legend = [ - { "Current": "var(--g-node-active)" }, - { "Note": "var(--g-node)" }, - ...pathColors - ] - legend.forEach((legendEntry, i) => { - const key = Object.keys(legendEntry)[0] - const colour = legendEntry[key] - svg.append("circle").attr("cx", -width / 2 + 20).attr("cy", height / 2 - 30 * (i + 1)).attr("r", 6).style("fill", colour) - svg.append("text").attr("x", -width / 2 + 40).attr("y", height / 2 - 30 * (i + 1)).text(key).style("font-size", "15px").attr("alignment-baseline", "middle") - }) - } - - // draw links between nodes - const link = svg.append("g") - .selectAll("line") - .data(data.links) - .join("line") - .attr("class", "link") - .attr("stroke", "var(--g-link)") - .attr("stroke-width", 2) - .attr("data-source", d => d.source.id) - .attr("data-target", d => d.target.id) - - // svg groups - const graphNode = svg.append("g") - .selectAll("g") - .data(data.nodes) - .enter().append("g") - - // draw individual nodes - const node = graphNode.append("circle") - .attr("class", "node") - .attr("id", (d) => d.id) - .attr("r", (d) => { - const numOut = index.links[d.id]?.length || 0 - const numIn = index.backlinks[d.id]?.length || 0 - return 3 + (numOut + numIn) / 4 - }) - .attr("fill", color) - .style("cursor", "pointer") - .on("click", (_, d) => { - window.location.href = baseUrl + '/' + decodeURI(d.id).replace(/\s+/g, '-') - }) - .on("mouseover", function(_, d) { - d3.selectAll(".node") - .transition() - .duration(100) - .attr("fill", "var(--g-node-inactive)") - - const neighbours = parseIdsFromLinks([...(index.links[d.id] || []), ...(index.backlinks[d.id] || [])]) - const neighbourNodes = d3.selectAll(".node").filter(d => neighbours.includes(d.id)) - const currentId = d.id - const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) - - // highlight neighbour nodes - neighbourNodes - .transition() - .duration(200) - .attr("fill", color) - - // highlight links - linkNodes - .transition() - .duration(200) - .attr("stroke", "var(--g-link-active)") - - // show text for self - d3.select(this.parentNode) - .select("text") - .raise() - .transition() - .duration(200) - .style("opacity", 1) - }).on("mouseleave", function(_, d) { - d3.selectAll(".node") - .transition() - .duration(200) - .attr("fill", color) - - const currentId = d.id - const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) - - linkNodes - .transition() - .duration(200) - .attr("stroke", "var(--g-link)") - - d3.select(this.parentNode) - .select("text") - .transition() - .duration(200) - .style("opacity", 0) - }) - .call(drag(simulation)); - - // draw labels - const labels = graphNode.append("text") - .attr("dx", 12) - .attr("dy", ".35em") - .text((d) => content[d.id]?.title || d.id.replace("-", " ")) - .style("opacity", 0) - .style("pointer-events", "none") - .call(drag(simulation)); - - // set panning - - if (enableZoom) { - svg.call(d3.zoom() - .extent([[0, 0], [width, height]]) - .scaleExtent([0.25, 4]) - .on("zoom", ({ transform }) => { - link.attr("transform", transform); - node.attr("transform", transform); - labels.attr("transform", transform); - })); - } - - // progress the simulation - simulation.on("tick", () => { - link - .attr("x1", d => d.source.x) - .attr("y1", d => d.source.y) - .attr("x2", d => d.target.x) - .attr("y2", d => d.target.y) - node - .attr("cx", d => d.x) - .attr("cy", d => d.y) - labels - .attr("x", d => d.x) - .attr("y", d => d.y) - }); -} +async function drawGraph( + baseUrl, + pathColors, + depth, + enableDrag, + enableLegend, + enableZoom +) { + const container = document.getElementById('graph-container') + const { index, links, content } = await fetchData + + // Use .pathname to remove hashes / searchParams / text fragments + const cleanUrl = window.location.origin + window.location.pathname + + const curPage = cleanUrl.replace(/\/$/g, "").replace(baseUrl, "") + + const parseIdsFromLinks = (links) => [ + ...new Set(links.flatMap((link) => [link.source, link.target])), + ] + + // Links is mutated by d3. We want to use links later on, so we make a copy and pass that one to d3 + // Note: shallow cloning does not work because it copies over references from the original array + const copyLinks = JSON.parse(JSON.stringify(links)) + + const neighbours = new Set() + const wl = [curPage || '/', '__SENTINEL'] + if (depth >= 0) { + while (depth >= 0 && wl.length > 0) { + // compute neighbours + const cur = wl.shift() + if (cur === '__SENTINEL') { + depth-- + wl.push('__SENTINEL') + } else { + neighbours.add(cur) + const outgoing = index.links[cur] || [] + const incoming = index.backlinks[cur] || [] + wl.push( + ...outgoing.map((l) => l.target), + ...incoming.map((l) => l.source) + ) + } + } + } else { + parseIdsFromLinks(copyLinks).forEach((id) => neighbours.add(id)) + } + + const data = { + nodes: [...neighbours].map((id) => ({ id })), + links: copyLinks.filter( + (l) => neighbours.has(l.source) && neighbours.has(l.target) + ), + } + + const color = (d) => { + if (d.id === curPage || (d.id === '/' && curPage === '')) { + return 'var(--g-node-active)' + } + + for (const pathColor of pathColors) { + const path = Object.keys(pathColor)[0] + const colour = pathColor[path] + if (d.id.startsWith(path)) { + return colour + } + } + + return 'var(--g-node)' + } + + const drag = (simulation) => { + function dragstarted(event, d) { + if (!event.active) simulation.alphaTarget(1).restart() + d.fx = d.x + d.fy = d.y + } + + function dragged(event, d) { + d.fx = event.x + d.fy = event.y + } + + function dragended(event, d) { + if (!event.active) simulation.alphaTarget(0) + d.fx = null + d.fy = null + } + + const noop = () => { } + return d3 + .drag() + .on('start', enableDrag ? dragstarted : noop) + .on('drag', enableDrag ? dragged : noop) + .on('end', enableDrag ? dragended : noop) + } + + const height = Math.max(container.offsetHeight, 250) + const width = container.offsetWidth + + const simulation = d3 + .forceSimulation(data.nodes) + .force('charge', d3.forceManyBody().strength(-30)) + .force( + 'link', + d3 + .forceLink(data.links) + .id((d) => d.id) + .distance(40) + ) + .force('center', d3.forceCenter()) + + const svg = d3 + .select('#graph-container') + .append('svg') + .attr('width', width) + .attr('height', height) + .attr('viewBox', [-width / 2, -height / 2, width, height]) + + if (enableLegend) { + const legend = [ + { Current: 'var(--g-node-active)' }, + { Note: 'var(--g-node)' }, + ...pathColors, + ] + legend.forEach((legendEntry, i) => { + const key = Object.keys(legendEntry)[0] + const colour = legendEntry[key] + svg + .append('circle') + .attr('cx', -width / 2 + 20) + .attr('cy', height / 2 - 30 * (i + 1)) + .attr('r', 6) + .style('fill', colour) + svg + .append('text') + .attr('x', -width / 2 + 40) + .attr('y', height / 2 - 30 * (i + 1)) + .text(key) + .style('font-size', '15px') + .attr('alignment-baseline', 'middle') + }) + } + + // draw links between nodes + const link = svg + .append('g') + .selectAll('line') + .data(data.links) + .join('line') + .attr('class', 'link') + .attr('stroke', 'var(--g-link)') + .attr('stroke-width', 2) + .attr('data-source', (d) => d.source.id) + .attr('data-target', (d) => d.target.id) + + // svg groups + const graphNode = svg + .append('g') + .selectAll('g') + .data(data.nodes) + .enter() + .append('g') + + // calculate radius + const nodeRadius = (d) => { + const numOut = index.links[d.id]?.length || 0 + const numIn = index.backlinks[d.id]?.length || 0 + return 3 + (numOut + numIn) / 4 + } + + // draw individual nodes + const node = graphNode + .append('circle') + .attr('class', 'node') + .attr('id', (d) => d.id) + .attr('r', nodeRadius) + .attr('fill', color) + .style('cursor', 'pointer') + .on('click', (_, d) => { + // SPA navigation + window.navigate( + new URL(`${baseUrl}${decodeURI(d.id).replace(/\s+/g, '-')}/`), + '.singlePage' + ) + }) + .on('mouseover', function(_, d) { + d3.selectAll('.node') + .transition() + .duration(100) + .attr('fill', 'var(--g-node-inactive)') + + const neighbours = parseIdsFromLinks([ + ...(index.links[d.id] || []), + ...(index.backlinks[d.id] || []), + ]) + const neighbourNodes = d3 + .selectAll('.node') + .filter((d) => neighbours.includes(d.id)) + const currentId = d.id + const linkNodes = d3 + .selectAll('.link') + .filter((d) => d.source.id === currentId || d.target.id === currentId) + + // highlight neighbour nodes + neighbourNodes.transition().duration(200).attr('fill', color) + + // highlight links + linkNodes + .transition() + .duration(200) + .attr('stroke', 'var(--g-link-active)') + + // show text for self + d3.select(this.parentNode) + .raise() + .select('text') + .transition() + .duration(200) + .style('opacity', 1) + }) + .on('mouseleave', function(_, d) { + d3.selectAll('.node').transition().duration(200).attr('fill', color) + + const currentId = d.id + const linkNodes = d3 + .selectAll('.link') + .filter((d) => d.source.id === currentId || d.target.id === currentId) + + linkNodes.transition().duration(200).attr('stroke', 'var(--g-link)') + + d3.select(this.parentNode) + .select('text') + .transition() + .duration(200) + .style('opacity', 0) + }) + .call(drag(simulation)) + + // draw labels + const labels = graphNode + .append('text') + .attr('dx', 0) + .attr('dy', (d) => nodeRadius(d) + 8 + 'px') + .attr('text-anchor', 'middle') + .text((d) => content[d.id]?.title || d.id.replace('-', ' ')) + .style('opacity', 0) + .style('pointer-events', 'none') + .style('font-size', '0.4em') + .raise() + .call(drag(simulation)) + + // set panning + + if (enableZoom) { + svg.call( + d3 + .zoom() + .extent([ + [0, 0], + [width, height], + ]) + .scaleExtent([0.25, 4]) + .on('zoom', ({ transform }) => { + link.attr('transform', transform) + node.attr('transform', transform) + const scale = transform.k + const scaledOpacity = Math.max((scale - 1) / 3.75, 0) + labels.attr('transform', transform).style('opacity', scaledOpacity) + }) + ) + } + + // progress the simulation + simulation.on('tick', () => { + link + .attr('x1', (d) => d.source.x) + .attr('y1', (d) => d.source.y) + .attr('x2', (d) => d.target.x) + .attr('y2', (d) => d.target.y) + node.attr('cx', (d) => d.x).attr('cy', (d) => d.y) + labels.attr('x', (d) => d.x).attr('y', (d) => d.y) + }) +} diff --git a/assets/js/popover.js b/assets/js/popover.js index 93ae12275..494cd84e1 100644 --- a/assets/js/popover.js +++ b/assets/js/popover.js @@ -1,35 +1,58 @@ -function htmlToElement(html) { - const template = document.createElement('template') - html = html.trim() - template.innerHTML = html - return template.content.firstChild -} - -function initPopover(baseURL) { - const basePath = baseURL.replace(window.location.origin, "") - document.addEventListener("DOMContentLoaded", () => { - fetchData.then(({ content }) => { - const links = [...document.getElementsByClassName("internal-link")] - links - .filter(li => li.dataset.src) - .forEach(li => { - const linkDest = content[li.dataset.src.replace(basePath, "")] - if (linkDest) { - const popoverElement = `
-

${linkDest.title}

-

${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...

-

${new Date(linkDest.lastmodified).toLocaleDateString()}

-
` - const el = htmlToElement(popoverElement) - li.appendChild(el) - li.addEventListener("mouseover", () => { - el.classList.add("visible") - }) - li.addEventListener("mouseout", () => { - el.classList.remove("visible") - }) - } - }) - }) - }) -} +function htmlToElement(html) { + const template = document.createElement("template") + html = html.trim() + template.innerHTML = html + return template.content.firstChild +} + +function initPopover(baseURL, useContextualBacklinks, renderLatex) { + const basePath = baseURL.replace(window.location.origin, "") + fetchData.then(({ content }) => { + const links = [...document.getElementsByClassName("internal-link")] + links + .filter(li => li.dataset.src || (li.dataset.idx && useContextualBacklinks)) + .forEach(li => { + var el + if (li.dataset.ctx) { + const linkDest = content[li.dataset.src] + const popoverElement = `
+

${linkDest.title}

+

${highlight(removeMarkdown(linkDest.content), li.dataset.ctx)}...

+

${new Date(linkDest.lastmodified).toLocaleDateString()}

+
` + el = htmlToElement(popoverElement) + } else { + const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")] + if (linkDest) { + const popoverElement = `
+

${linkDest.title}

+

${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...

+

${new Date(linkDest.lastmodified).toLocaleDateString()}

+
` + el = htmlToElement(popoverElement) + } + } + + if (el) { + li.appendChild(el) + if (renderLatex) { + renderMathInElement(el, { + delimiters: [ + { left: '$$', right: '$$', display: false }, + { left: '$', right: '$', display: false }, + { left: '\\(', right: '\\)', display: false }, + { left: '\\[', right: '\\]', display: false } + ], + throwOnError: false + }) + } + li.addEventListener("mouseover", () => { + el.classList.add("visible") + }) + li.addEventListener("mouseout", () => { + el.classList.remove("visible") + }) + } + }) + }) +} diff --git a/assets/js/router.js b/assets/js/router.js new file mode 100644 index 000000000..81c25ac1c --- /dev/null +++ b/assets/js/router.js @@ -0,0 +1,12 @@ +import { router, navigate } from "https://unpkg.com/million@1.8.9-0/dist/router.mjs" + +export const attachSPARouting = (draw) => { + // SPA navigation for access later + window.navigate = navigate + // We only mutate document.title and content within .singlePage element + router(".singlePage") + // We need on initial load, then subsequent redirs + // requestAnimationFrame() delays graph draw until SPA routing is finished + window.addEventListener("million:navigate", () => requestAnimationFrame(draw)) + window.addEventListener("DOMContentLoaded", () => requestAnimationFrame(draw)) +} diff --git a/assets/js/search.js b/assets/js/search.js index f155590b9..bb94cd30b 100644 --- a/assets/js/search.js +++ b/assets/js/search.js @@ -1,239 +1,270 @@ -// code from https://github.com/danestves/markdown-to-text -const removeMarkdown = ( - markdown, - options = { - listUnicodeChar: false, - stripListLeaders: true, - gfm: true, - useImgAltText: false, - preserveLinks: false, - } -) => { - let output = markdown || ""; - output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, ""); - - try { - if (options.stripListLeaders) { - if (options.listUnicodeChar) - output = output.replace( - /^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, - options.listUnicodeChar + " $1" - ); - else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, "$1"); - } - if (options.gfm) { - output = output - .replace(/\n={2,}/g, "\n") - .replace(/~{3}.*\n/g, "") - .replace(/~~/g, "") - .replace(/`{3}.*\n/g, ""); - } - if (options.preserveLinks) { - output = output.replace(/\[(.*?)\][\[\(](.*?)[\]\)]/g, "$1 ($2)") - } - output = output - .replace(/<[^>]*>/g, "") - .replace(/^[=\-]{2,}\s*$/g, "") - .replace(/\[\^.+?\](\: .*?$)?/g, "") - .replace(/\s{0,2}\[.*?\]: .*?$/g, "") - .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? "$1" : "") - .replace(/\[(.*?)\][\[\(].*?[\]\)]/g, "$1") - .replace(/^\s{0,3}>\s?/g, "") - .replace(/(^|\n)\s{0,3}>\s?/g, "\n\n") - .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "") - .replace( - /^(\n)?\s{0,}#{1,6}\s+| {0,}(\n)?\s{0,}#{0,} {0,}(\n)?\s{0,}$/gm, - "$1$2$3" - ) - .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, "$2") - .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, "$2") - .replace(/(`{3,})(.*?)\1/gm, "$2") - .replace(/`(.+?)`/g, "$1") - .replace(/\n{2,}/g, "\n\n"); - } catch (e) { - console.error(e); - return markdown; - } - return output; -}; -// ----- - -(async function() { - const encoder = str => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])+/) - const contentIndex = new FlexSearch.Document({ - cache: true, - charset: "latin:extra", - optimize: true, - index: [{ - field: "content", - tokenize: "reverse", - encode: encoder, - }, { - field: "title", - tokenize: "forward", - encode: encoder, - }] - }) - - const { content } = await fetchData - for (const [key, value] of Object.entries(content)) { - contentIndex.add({ - id: key, - title: value.title, - content: removeMarkdown(value.content), - }) - } - - const highlight = (content, term) => { - const highlightWindow = 20 - const tokenizedTerm = term.split(/\s+/).filter(t => t !== "") - const splitText = content.split(/\s+/).filter(t => t !== "") - const includesCheck = (token) => tokenizedTerm.some(term => token.toLowerCase().startsWith(term.toLowerCase())) - - const occurrencesIndices = splitText - .map(includesCheck) - - // calculate best index - let bestSum = 0 - let bestIndex = 0 - for (let i = 0; i < Math.max(occurrencesIndices.length - highlightWindow, 0); i++) { - const window = occurrencesIndices.slice(i, i + highlightWindow) - const windowSum = window.reduce((total, cur) => total + cur, 0) - if (windowSum >= bestSum) { - bestSum = windowSum - bestIndex = i - } - } - - const startIndex = Math.max(bestIndex - highlightWindow, 0) - const endIndex = Math.min(startIndex + 2 * highlightWindow, splitText.length) - const mappedText = splitText - .slice(startIndex, endIndex) - .map(token => { - if (includesCheck(token)) { - return `${token}` - } - return token - }) - .join(" ") - .replaceAll(' ', " ") - return `${startIndex === 0 ? "" : "..."}${mappedText}${endIndex === splitText.length ? "" : "..."}` - } - - const resultToHTML = ({ url, title, content, term }) => { - const text = removeMarkdown(content) - const resultTitle = highlight(title, term) - const resultText = highlight(text, term) - return `` - } - - const redir = (id, term) => { - window.location.href = BASE_URL + `${id}#:~:text=${encodeURIComponent(term)}` - } - - const formatForDisplay = id => ({ - id, - url: id, - title: content[id].title, - content: content[id].content - }) - - const source = document.getElementById('search-bar') - const results = document.getElementById("results-container") - let term - source.addEventListener("keyup", (e) => { - if (e.key === "Enter") { - const anchor = document.getElementsByClassName("result-card")[0] - redir(anchor.id, term) - } - }) - source.addEventListener('input', (e) => { - term = e.target.value - const searchResults = contentIndex.search(term, [ - { - field: "content", - limit: 10, - }, - { - field: "title", - limit: 5, - } - ]) - const getByField = field => { - const results = searchResults.filter(x => x.field === field) - if (results.length === 0) { - return [] - } else { - return [...results[0].result] - } - } - const allIds = new Set([...getByField('title'), ...getByField('content')]) - const finalResults = [...allIds].map(formatForDisplay) - - // display - if (finalResults.length === 0) { - results.innerHTML = `` - } else { - results.innerHTML = finalResults - .map(result => resultToHTML({ - ...result, - term, - })) - .join("\n") - const anchors = document.getElementsByClassName("result-card"); - [...anchors].forEach(anchor => { - anchor.onclick = () => redir(anchor.id, term) - }) - } - }) - - - const searchContainer = document.getElementById("search-container") - - function openSearch() { - if (searchContainer.style.display === "none" || searchContainer.style.display === "") { - source.value = "" - results.innerHTML = "" - searchContainer.style.display = "block" - source.focus() - } else { - searchContainer.style.display = "none" - } - } - - function closeSearch() { - searchContainer.style.display = "none" - } - - document.addEventListener('keydown', (event) => { - if (event.key === "k" && (event.ctrlKey || event.metaKey)) { - event.preventDefault() - openSearch() - } - if (event.key === "Escape") { - event.preventDefault() - closeSearch() - } - }) - - const searchButton = document.getElementById("search-icon") - searchButton.addEventListener('click', (evt) => { - openSearch() - }) - searchButton.addEventListener('keydown', (evt) => { - openSearch() - }) - searchContainer.addEventListener('click', (evt) => { - closeSearch() - }) - document.getElementById("search-space").addEventListener('click', (evt) => { - evt.stopPropagation() - }) -})() - +// code from https://github.com/danestves/markdown-to-text +const removeMarkdown = ( + markdown, + options = { + listUnicodeChar: false, + stripListLeaders: true, + gfm: true, + useImgAltText: false, + preserveLinks: false, + } +) => { + let output = markdown || '' + output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, '') + + try { + if (options.stripListLeaders) { + if (options.listUnicodeChar) + output = output.replace( + /^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, + options.listUnicodeChar + ' $1' + ) + else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1') + } + if (options.gfm) { + output = output + .replace(/\n={2,}/g, '\n') + .replace(/~{3}.*\n/g, '') + .replace(/~~/g, '') + .replace(/`{3}.*\n/g, '') + } + if (options.preserveLinks) { + output = output.replace(/\[(.*?)\][\[\(](.*?)[\]\)]/g, '$1 ($2)') + } + output = output + .replace(/<[^>]*>/g, '') + .replace(/^[=\-]{2,}\s*$/g, '') + .replace(/\[\^.+?\](\: .*?$)?/g, '') + .replace(/(#{1,6})\s+(.+)\1?/g, '$2') + .replace(/\s{0,2}\[.*?\]: .*?$/g, '') + .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? '$1' : '') + .replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '$1') + .replace(/!?\[\[\S[^\[\]\|]*(?:\|([^\[\]]*))?\S\]\]/g, '$1') + .replace(/^\s{0,3}>\s?/g, '') + .replace(/(^|\n)\s{0,3}>\s?/g, '\n\n') + .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '') + .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, '$2') + .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, '$2') + .replace(/(`{3,})(.*?)\1/gm, '$2') + .replace(/`(.+?)`/g, '$1') + .replace(/\n{2,}/g, '\n\n') + } catch (e) { + console.error(e) + return markdown + } + return output +} +// ----- + +const highlight = (content, term) => { + const highlightWindow = 20 + + // try to find direct match first + const directMatchIdx = content.indexOf(term) + if (directMatchIdx !== -1) { + const h = highlightWindow / 2 + const before = content.substring(0, directMatchIdx).split(" ").slice(-h) + const after = content.substring(directMatchIdx + term.length, content.length - 1).split(" ").slice(0, h) + return (before.length == h ? `...${before.join(" ")}` : before.join(" ")) + `${term}` + after.join(" ") + } + + const tokenizedTerm = term.split(/\s+/).filter((t) => t !== '') + const splitText = content.split(/\s+/).filter((t) => t !== '') + const includesCheck = (token) => + tokenizedTerm.some((term) => + token.toLowerCase().startsWith(term.toLowerCase()) + ) + + const occurrencesIndices = splitText.map(includesCheck) + + // calculate best index + let bestSum = 0 + let bestIndex = 0 + for ( + let i = 0; + i < Math.max(occurrencesIndices.length - highlightWindow, 0); + i++ + ) { + const window = occurrencesIndices.slice(i, i + highlightWindow) + const windowSum = window.reduce((total, cur) => total + cur, 0) + if (windowSum >= bestSum) { + bestSum = windowSum + bestIndex = i + } + } + + const startIndex = Math.max(bestIndex - highlightWindow, 0) + const endIndex = Math.min( + startIndex + 2 * highlightWindow, + splitText.length + ) + const mappedText = splitText + .slice(startIndex, endIndex) + .map((token) => { + if (includesCheck(token)) { + return `${token}` + } + return token + }) + .join(' ') + .replaceAll(' ', ' ') + return `${startIndex === 0 ? '' : '...'}${mappedText}${endIndex === splitText.length ? '' : '...' + }` +}; + +(async function() { + const encoder = (str) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])+/) + const contentIndex = new FlexSearch.Document({ + cache: true, + charset: 'latin:extra', + optimize: true, + index: [ + { + field: 'content', + tokenize: 'reverse', + encode: encoder, + }, + { + field: 'title', + tokenize: 'forward', + encode: encoder, + }, + ], + }) + + const { content } = await fetchData + for (const [key, value] of Object.entries(content)) { + contentIndex.add({ + id: key, + title: value.title, + content: removeMarkdown(value.content), + }) + } + + const resultToHTML = ({ url, title, content, term }) => { + const text = removeMarkdown(content) + const resultTitle = highlight(title, term) + const resultText = highlight(text, term) + return `` + } + + const redir = (id, term) => { + // SPA navigation + window.navigate( + new URL( + `${BASE_URL.replace(/\/$/g, "")}${id}#:~:text=${encodeURIComponent(term)}/` + ), + '.singlePage' + ) + closeSearch() + } + + const formatForDisplay = (id) => ({ + id, + url: id, + title: content[id].title, + content: content[id].content, + }) + + const source = document.getElementById('search-bar') + const results = document.getElementById('results-container') + let term + source.addEventListener('keyup', (e) => { + if (e.key === 'Enter') { + const anchor = document.getElementsByClassName('result-card')[0] + redir(anchor.id, term) + } + }) + source.addEventListener('input', (e) => { + term = e.target.value + const searchResults = contentIndex.search(term, [ + { + field: 'content', + limit: 10, + }, + { + field: 'title', + limit: 5, + }, + ]) + const getByField = (field) => { + const results = searchResults.filter((x) => x.field === field) + if (results.length === 0) { + return [] + } else { + return [...results[0].result] + } + } + const allIds = new Set([...getByField('title'), ...getByField('content')]) + const finalResults = [...allIds].map(formatForDisplay) + + // display + if (finalResults.length === 0) { + results.innerHTML = `` + } else { + results.innerHTML = finalResults + .map((result) => + resultToHTML({ + ...result, + term, + }) + ) + .join('\n') + const anchors = [...document.getElementsByClassName('result-card')] + anchors.forEach((anchor) => { + anchor.onclick = () => redir(anchor.id, term) + }) + } + }) + + const searchContainer = document.getElementById('search-container') + + function openSearch() { + if ( + searchContainer.style.display === 'none' || + searchContainer.style.display === '' + ) { + source.value = '' + results.innerHTML = '' + searchContainer.style.display = 'block' + source.focus() + } else { + searchContainer.style.display = 'none' + } + } + + function closeSearch() { + searchContainer.style.display = 'none' + } + + document.addEventListener('keydown', (event) => { + if (event.key === 'k' && (event.ctrlKey || event.metaKey)) { + event.preventDefault() + openSearch() + } + if (event.key === 'Escape') { + event.preventDefault() + closeSearch() + } + }) + + const searchButton = document.getElementById('search-icon') + searchButton.addEventListener('click', (evt) => { + openSearch() + }) + searchButton.addEventListener('keydown', (evt) => { + openSearch() + }) + searchContainer.addEventListener('click', (evt) => { + closeSearch() + }) + document.getElementById('search-space').addEventListener('click', (evt) => { + evt.stopPropagation() + }) +})() diff --git a/assets/styles/base.scss b/assets/styles/base.scss index 68cbb213a..1b9b936e3 100644 --- a/assets/styles/base.scss +++ b/assets/styles/base.scss @@ -1,571 +1,591 @@ -:root { - --lt-colours-light: var(--light) !important; - --lt-colours-lightgray: var(--lightgray) !important; - --lt-colours-dark: var(--secondary) !important; - --lt-colours-secondary: var(--tertiary) !important; - --lt-colours-gray: var(--outlinegray) !important; -} - -h1, h2, h3, h4, h5, h6, ol, ul, thead { - font-family: Inter; - color: var(--dark); - font-weight: revert; - margin: revert; - padding: revert; -} - -p, ul, text { - font-family: 'Source Sans Pro', sans-serif; - color: var(--gray); - fill: var(--gray); - font-weight: revert; - margin: revert; - padding: revert; -} - -.mainTOC { - background: var(--lightgray); - border-radius: 5px; - padding: 0.75em 1em; -} - -.mainTOC details summary { - cursor: zoom-in; - font-family: Inter; - color: var(--dark); - font-weight: 700; -} - -.mainTOC details[open] summary { - cursor: zoom-out; -} - -#TableOfContents > ol { - counter-reset: section; - margin-left: 0em; - padding-left: 1.5em; - & > li { - counter-increment: section; - & > ol { - counter-reset: subsection; - & > li { - counter-increment: subsection; - &::marker { - content: counter(section) "." counter(subsection) " "; - } - } - } - } - - & > li::marker { - content: counter(section) " "; - } - - & > li::marker, & > li > ol > li::marker { - font-family: Source Sans Pro; - font-weight: 700; - } -} - -table { - width: 100%; -} - -img { - width: 100%; - border-radius: 3px; - margin: 1em 0; -} - -p>img+em { - display: block; - transform: translateY(-1em); -} - -sup { - line-height: 0 -} - -p, tbody, li { - font-family: Source Sans Pro; - color: var(--gray); - line-height: 1.5em; -} - -blockquote { - margin-left: 0em; - border-left: 3px solid var(--secondary); - padding-left: 1em; - transition: border-color 0.2s ease; - - &:hover { - border-color: var(--tertiary); - } -} - -table { - padding: 1.5em; -} - -td, th { - padding: 0.1em 0.5em; -} - -.footnotes p { - margin: 0.5em 0; -} - -.pagination { - list-style: none; - padding-left: 0; - display: flex; - margin-top: 2em; - gap: 1.5em; - justify-content: center; - - .disabled { - opacity: 0.2; - } - - & > li { - text-align: center; - display: inline-block; - - & a { - background-color: transparent !important; - } - - & a[href$="#"] { - opacity: 0.2; - } - } -} - -.section { - & h3 > a { - font-weight: 700; - font-family: Inter; - margin: 0; - } - & p { - margin-top: 0; - } -} - -article { - & > .meta { - margin: -1.5em 0 1em 0; - opacity: 0.7; - } - - & > .tags { - list-style: none; - padding-left: 0; - - & .meta { - & > h1 { - margin: 0; - } - & > p { - margin: 0; - } - } - - & > li { - display: inline-block; - } - & > li > a { - border-radius: 8px; - border: var(--outlinegray) 1px solid; - padding: 0.2em 0.5em; - &::before { - content: "#"; - margin-right: 0.3em; - color: var(--outlinegray); - } - } - } - - & a { - font-family: Source Sans Pro; - font-weight: 600; - - &.internal-link { - text-decoration: none; - background-color: transparentize(#8f9fa9, 0.85); - padding: 0 0.1em; - margin: auto -0.1em; - border-radius: 3px; - - &.broken { - opacity: 0.5; - background-color: transparent; - } - } - } - - & p { - overflow-wrap: anywhere; - } -} - -.backlinks a { - font-weight: 600; - font-size: 0.9rem; -} - -sup > a { - text-decoration: none; - padding: 0 0.1em 0 0.2em; -} - -a { - font-family: Inter, sans-serif; - font-size: 1em; - font-weight: 700; - text-decoration: none; - transition: all 0.2s ease; - color: var(--secondary); - - &:hover { - color: var(--tertiary) !important; - } -} - -pre { - font-family: 'Fira Code'; - padding: 0.75em; - border-radius: 3px; - overflow-x: scroll; -} - -code { - font-family: 'Fira Code'; - font-size: 0.85em; - padding: 0.15em 0.3em; - border-radius: 5px; - background: var(--lightgray); -} - -html { - scroll-behavior: smooth; - - &:lang(ar) { - & p, & h1, & h2, & h3, article { - direction: rtl; - text-align: right; - } - } -} - -body { - margin: 0; - height: 100vh; - width: 100vw; - //overflow-x: hidden; - max-width: 100%; - box-sizing: border-box; - background-color: var(--light); -} - -@keyframes fadeIn { - 0% {opacity:0;} - 100% {opacity:1;} -} - -footer { - margin-top: 4em; - text-align: center; - & ul { - padding-left: 0; - } -} - -hr { - width: 25%; - margin: 4em auto; - height: 2px; - border-radius: 1px; - border-width: 0; - color: var(--dark); - background-color: var(--dark); -} - -.singlePage { - padding: 4em 30vw; - - @media all and (max-width: 1200px) { - padding: 25px 5vw; - } -} - -.page-end { - display: flex; - flex-direction: row; - gap: 2em; - - @media all and (max-width: 780px) { - flex-direction: column; - } - - & > * { - flex: 1 0 0; - } - - & > .backlinks-container { - & > ul { - list-style: none; - padding-left: 0; - - & > li { - margin: 0.5em 0; - padding: 0.25em 1em; - border: var(--outlinegray) 1px solid; - border-radius: 5px - } - } - } - - & #graph-container { - border: var(--outlinegray) 1px solid; - border-radius: 5px; - } -} - -.centered { - margin-top: 30vh; -} - -article > h1 { - font-size: 2em; -} - -header { - display: flex; - flex-direction: row; - align-items: center; - - & > h1 { - font-size: 2em; - } - - & > nav { - @media all and (max-width: 600px) { - display: none; - } - } - - & > .spacer { - flex: 1 1 auto; - } - - & > svg { - cursor: pointer; - width: 18px; - min-width: 18px; - margin: 0 1em; - - &:hover .search-path { - stroke: var(--tertiary); - } - - .search-path { - stroke: var(--gray); - stroke-width: 2px; - transition: stroke 0.5s ease; - } - } -} - -#search-container { - position: fixed; - z-index: 9999; - left: 0; - top: 0; - width: 100vw; - height: 100%; - overflow: scroll; - display: none; - backdrop-filter: blur(4px); - -webkit-backdrop-filter: blur(4px); - - & > div { - width: 50%; - margin-top: 15vh; - margin-left: auto; - margin-right: auto; - - @media all and (max-width: 1200px) { - width: 90%; - } - - & > * { - width: 100%; - border-radius: 4px; - background: var(--light); - box-shadow: 0 14px 50px rgba(27, 33, 48, 0.12), 0 10px 30px rgba(27, 33, 48, 0.16); - margin-bottom: 2em; - } - - & > input { - box-sizing: border-box; - padding: 0.5em 1em; - font-family: Inter, sans-serif; - color: var(--dark); - font-size: 1.1em; - border: 1px solid var(--outlinegray); - - &:focus { - outline: none; - } - } - - & > #results-container { - & > .result-card { - padding: 1em; - cursor: pointer; - transition: background 0.2s ease; - border: 1px solid var(--outlinegray); - border-bottom: none; - width: 100%; - - // normalize button props - font-family: inherit; - font-size: 100%; - line-height: 1.15; - margin: 0; - overflow: visible; - text-transform: none; - text-align: left; - background: var(--light); - outline: none; - - &:hover, &:focus { - background: rgba(180, 180, 180, 0.15); - } - - &:first-of-type { - border-top-left-radius: 5px; - border-top-right-radius: 5px; - } - - &:last-of-type { - border-bottom-left-radius: 5px; - border-bottom-right-radius: 5px; - border-bottom: 1px solid var(--outlinegray); - } - - & > h3, & > p { - margin: 0; - } - - & .search-highlight { - background-color: #afbfc966; - padding: 0.05em 0.2em; - border-radius: 3px; - } - } - } - } -} - -.section-ul { - list-style: none; - padding-left: 0; - - & > li { - border: 1px solid var(--outlinegray); - border-radius: 5px; - padding: 0 1em; - margin-bottom: 1em; - - & h3 { - opacity: 1; - font-weight: 700; - margin-bottom: 0em; - } - - & .meta { - opacity: 0.6; - } - } -} - -@keyframes dropin { - 0% { - display: none; - opacity: 0; - visibility: hidden; - } - 1% { - display: inline-block; - opacity: 0; - transform: translate(-50%, 40%); - } - 100% { - opacity: 1; - visibility: visible; - transform: translate(-50%, 20%); - } -} - -.popover { - z-index: 999; - position: absolute; - width: 20em; - display: none; - background-color: var(--light); - padding: 1em; - border: 1px solid var(--outlinegray); - border-radius: 5px; - transform: translate(-50%, 40%); - pointer-events: none; - transition: opacity 0.2s ease, transform 0.2s ease; - user-select: none; - overflow-wrap: anywhere; - box-shadow: 6px 6px 36px 0px rgba(0,0,0,0.25); - - @media all and (max-width: 600px) { - display: none; - } - - &.visible { - opacity: 1; - visibility: visible; - transform: translate(-50%, 20%); - display: inline-block; - animation: dropin 0.2s ease; - } - - & > h3 { - font-size: 1rem; - margin: 0.25em 0; - } - - & > .meta { - margin-top: 0.25em; - opacity: 0.5; - font-family: "JetBrains Mono", monospace; - font-size: 0.8rem; - } - - & > p { - margin: 0; - font-weight: 400; - user-select: none; - } -} - - - -#contact_buttons ul { - list-style-type: none; - - li { - display: inline-block; - } - - li a { - padding: 0 1em; - } -} +:root { + --lt-colours-light: var(--light) !important; + --lt-colours-lightgray: var(--lightgray) !important; + --lt-colours-dark: var(--secondary) !important; + --lt-colours-secondary: var(--tertiary) !important; + --lt-colours-gray: var(--outlinegray) !important; +} + +h1, h2, h3, h4, h5, h6, ol, ul, thead { + font-family: Inter; + color: var(--dark); + font-weight: revert; + margin: revert; + padding: revert; + + &:hover > .hanchor { + opacity: 1; + } +} + +.hanchor { + font-family: Inter; + margin-left: -1em; + opacity: 0.3; + transition: opacity 0.3s ease; + color: var(--secondary); + +} + +p, ul, text { + font-family: 'Source Sans Pro', sans-serif; + color: var(--gray); + fill: var(--gray); + font-weight: revert; + margin: revert; + padding: revert; +} + +.mainTOC { + background: var(--lightgray); + border-radius: 5px; + padding: 0.75em 1em; +} + +.mainTOC details summary { + cursor: zoom-in; + font-family: Inter; + color: var(--dark); + font-weight: 700; +} + +.mainTOC details[open] summary { + cursor: zoom-out; +} + +#TableOfContents > ol { + counter-reset: section; + margin-left: 0em; + padding-left: 1.5em; + & > li { + counter-increment: section; + & > ol { + counter-reset: subsection; + & > li { + counter-increment: subsection; + &::marker { + content: counter(section) "." counter(subsection) " "; + } + } + } + } + + & > li::marker { + content: counter(section) " "; + } + + & > li::marker, & > li > ol > li::marker { + font-family: Source Sans Pro; + font-weight: 700; + } +} + +table { + width: 100%; +} + +img { + width: 100%; + border-radius: 3px; + margin: 1em 0; +} + +p>img+em { + display: block; + transform: translateY(-1em); +} + +sup { + line-height: 0 +} + +p, tbody, li { + font-family: Source Sans Pro; + color: var(--gray); + line-height: 1.5em; +} + +blockquote { + margin-left: 0em; + border-left: 3px solid var(--secondary); + padding-left: 1em; + transition: border-color 0.2s ease; + + &:hover { + border-color: var(--tertiary); + } +} + +table { + padding: 1.5em; +} + +td, th { + padding: 0.1em 0.5em; +} + +.footnotes p { + margin: 0.5em 0; +} + +.pagination { + list-style: none; + padding-left: 0; + display: flex; + margin-top: 2em; + gap: 1.5em; + justify-content: center; + + .disabled { + opacity: 0.2; + } + + & > li { + text-align: center; + display: inline-block; + + & a { + background-color: transparent !important; + } + + & a[href$="#"] { + opacity: 0.2; + } + } +} + +.section { + & h3 > a { + font-weight: 700; + font-family: Inter; + margin: 0; + } + & p { + margin-top: 0; + } +} + +article { + & > .meta { + margin: -1.5em 0 1em 0; + opacity: 0.7; + } + + & > .tags { + list-style: none; + padding-left: 0; + + & .meta { + & > h1 { + margin: 0; + } + & > p { + margin: 0; + } + } + + & > li { + display: inline-block; + } + & > li > a { + border-radius: 8px; + border: var(--outlinegray) 1px solid; + padding: 0.2em 0.5em; + &::before { + content: "#"; + margin-right: 0.3em; + color: var(--outlinegray); + } + } + } + + & a { + font-family: Source Sans Pro; + font-weight: 600; + + &.internal-link { + text-decoration: none; + background-color: transparentize(#8f9fa9, 0.85); + padding: 0 0.1em; + margin: auto -0.1em; + border-radius: 3px; + + &.broken { + opacity: 0.5; + background-color: transparent; + } + } + } + + & p { + overflow-wrap: anywhere; + } +} + +.backlinks a { + font-weight: 600; + font-size: 0.9rem; +} + +sup > a { + text-decoration: none; + padding: 0 0.1em 0 0.2em; +} + +a { + font-family: Inter, sans-serif; + font-size: 1em; + font-weight: 700; + text-decoration: none; + transition: all 0.2s ease; + color: var(--secondary); + + &:hover { + color: var(--tertiary) !important; + } +} + +pre { + font-family: 'Fira Code'; + padding: 0.75em; + border-radius: 3px; + overflow-x: scroll; +} + +code { + font-family: 'Fira Code'; + font-size: 0.85em; + padding: 0.15em 0.3em; + border-radius: 5px; + background: var(--lightgray); +} + +html { + scroll-behavior: smooth; + + &:lang(ar) { + & p, & h1, & h2, & h3, article { + direction: rtl; + text-align: right; + } + } +} + +body { + margin: 0; + height: 100vh; + width: 100vw; + //overflow-x: hidden; + max-width: 100%; + box-sizing: border-box; + background-color: var(--light); +} + +@keyframes fadeIn { + 0% {opacity:0;} + 100% {opacity:1;} +} + +footer { + margin-top: 4em; + text-align: center; + & ul { + padding-left: 0; + } +} + +hr { + width: 25%; + margin: 4em auto; + height: 2px; + border-radius: 1px; + border-width: 0; + color: var(--dark); + background-color: var(--dark); +} + +.singlePage { + padding: 4em 30vw; + + @media all and (max-width: 1200px) { + padding: 25px 5vw; + } +} + +.page-end { + display: flex; + flex-direction: row; + gap: 2em; + + @media all and (max-width: 780px) { + flex-direction: column; + } + + & > * { + flex: 1 0 0; + } + + & > .backlinks-container { + & > ul { + list-style: none; + padding-left: 0; + + & > li { + margin: 0.5em 0; + padding: 0.25em 1em; + border: var(--outlinegray) 1px solid; + border-radius: 5px + } + } + } + + & #graph-container { + border: var(--outlinegray) 1px solid; + border-radius: 5px; + box-sizing: border-box; + min-height: 250px; + + & > svg { + margin-bottom: -5px; + + } + } +} + +.centered { + margin-top: 30vh; +} + +article > h1 { + font-size: 2em; +} + +header { + display: flex; + flex-direction: row; + align-items: center; + + & > h1 { + font-size: 2em; + } + + & > nav { + @media all and (max-width: 600px) { + display: none; + } + } + + & > .spacer { + flex: 1 1 auto; + } + + & > svg { + cursor: pointer; + width: 18px; + min-width: 18px; + margin: 0 1em; + + &:hover .search-path { + stroke: var(--tertiary); + } + + .search-path { + stroke: var(--gray); + stroke-width: 2px; + transition: stroke 0.5s ease; + } + } +} + +#search-container { + position: fixed; + z-index: 9999; + left: 0; + top: 0; + width: 100vw; + height: 100%; + overflow: scroll; + display: none; + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + + & > div { + width: 50%; + margin-top: 15vh; + margin-left: auto; + margin-right: auto; + + @media all and (max-width: 1200px) { + width: 90%; + } + + & > * { + width: 100%; + border-radius: 4px; + background: var(--light); + box-shadow: 0 14px 50px rgba(27, 33, 48, 0.12), 0 10px 30px rgba(27, 33, 48, 0.16); + margin-bottom: 2em; + } + + & > input { + box-sizing: border-box; + padding: 0.5em 1em; + font-family: Inter, sans-serif; + color: var(--dark); + font-size: 1.1em; + border: 1px solid var(--outlinegray); + + &:focus { + outline: none; + } + } + + & > #results-container { + & .result-card { + padding: 1em; + cursor: pointer; + transition: background 0.2s ease; + border: 1px solid var(--outlinegray); + border-bottom: none; + width: 100%; + + // normalize button props + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; + overflow: visible; + text-transform: none; + text-align: left; + background: var(--light); + outline: none; + + &:hover, &:focus { + background: rgba(180, 180, 180, 0.15); + } + + &:first-of-type { + border-top-left-radius: 5px; + border-top-right-radius: 5px; + } + + &:last-of-type { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border-bottom: 1px solid var(--outlinegray); + } + + & > h3, & > p { + margin: 0; + } + } + } + } +} + +.search-highlight { + background-color: #afbfc966; + padding: 0.05em 0.2em; + border-radius: 3px; +} + +.section-ul { + list-style: none; + padding-left: 0; + + & > li { + border: 1px solid var(--outlinegray); + border-radius: 5px; + padding: 0 1em; + margin-bottom: 1em; + + & h3 { + opacity: 1; + font-weight: 700; + margin-bottom: 0em; + } + + & .meta { + opacity: 0.6; + } + } +} + +@keyframes dropin { + 0% { + display: none; + opacity: 0; + visibility: hidden; + } + 1% { + display: inline-block; + opacity: 0; + transform: translate(-50%, 40%); + } + 100% { + opacity: 1; + visibility: visible; + transform: translate(-50%, 20%); + } +} + +.popover { + z-index: 999; + position: absolute; + width: 20em; + display: none; + background-color: var(--light); + padding: 1em; + border: 1px solid var(--outlinegray); + border-radius: 5px; + transform: translate(-50%, 40%); + pointer-events: none; + transition: opacity 0.2s ease, transform 0.2s ease; + user-select: none; + overflow-wrap: anywhere; + box-shadow: 6px 6px 36px 0px rgba(0,0,0,0.25); + + @media all and (max-width: 600px) { + display: none !important; + } + + &.visible { + opacity: 1; + visibility: visible; + transform: translate(-50%, 20%); + display: inline-block; + animation: dropin 0.2s ease; + } + + & > h3 { + font-size: 1rem; + margin: 0.25em 0; + } + + & > .meta { + margin-top: 0.25em; + opacity: 0.5; + font-family: "JetBrains Mono", monospace; + font-size: 0.8rem; + } + + & > p, & > a { + margin: 0; + font-weight: 400; + user-select: none; + } +} + + + +#contact_buttons ul { + list-style-type: none; + + li { + display: inline-block; + } + + li a { + padding: 0 1em; + } +} diff --git a/assets/styles/darkmode.scss b/assets/styles/darkmode.scss index dc293b66c..61967d797 100644 --- a/assets/styles/darkmode.scss +++ b/assets/styles/darkmode.scss @@ -1,44 +1,44 @@ -.darkmode { - float: right; - padding: 1em; - min-width: 30px; - position: relative; - - @media all and (max-width: 450px) { - padding: 1em; - } - - & > .toggle { - display: none; - box-sizing: border-box; - } - - & svg { - opacity: 0; - position: absolute; - width: 20px; - height: 20px; - top: calc(50% - 10px); - margin: 0 7px; - fill: var(--gray); - transition: opacity 0.1s ease; - } -} - -.toggle:checked ~ label { - & > #dayIcon { - opacity: 0; - } - & > #nightIcon { - opacity: 1; - } -} - -.toggle:not(:checked) ~ label { - & > #dayIcon { - opacity: 1; - } - & > #nightIcon { - opacity: 0; - } +.darkmode { + float: right; + padding: 1em; + min-width: 30px; + position: relative; + + @media all and (max-width: 450px) { + padding: 1em; + } + + & > .toggle { + display: none; + box-sizing: border-box; + } + + & svg { + opacity: 0; + position: absolute; + width: 20px; + height: 20px; + top: calc(50% - 10px); + margin: 0 7px; + fill: var(--gray); + transition: opacity 0.1s ease; + } +} + +.toggle:checked ~ label { + & > #dayIcon { + opacity: 0; + } + & > #nightIcon { + opacity: 1; + } +} + +.toggle:not(:checked) ~ label { + & > #dayIcon { + opacity: 1; + } + & > #nightIcon { + opacity: 0; + } } \ No newline at end of file diff --git a/data/graphConfig.yaml b/data/graphConfig.yaml index 7ab89bf80..3f8d3fb6b 100644 --- a/data/graphConfig.yaml +++ b/data/graphConfig.yaml @@ -1,6 +1,6 @@ -enableLegend: false -enableDrag: true -enableZoom: true -depth: -1 # set to -1 to show full graph -paths: +enableLegend: false +enableDrag: true +enableZoom: true +depth: -1 # set to -1 to show full graph +paths: - /moc: "#4388cc" \ No newline at end of file diff --git a/layouts/404.html b/layouts/404.html index 27f2ab857..424839502 100644 --- a/layouts/404.html +++ b/layouts/404.html @@ -1,16 +1,16 @@ - - -{{ partial "head.html" . }} - - -
- {{partial "darkmode.html" .}} -
-

404.

-

Hey! You look a little lost. This page doesn't exist (or may be private).

- ↳ Let's get you home. -
-
- - - + + +{{ partial "head.html" . }} + + +
+ {{partial "darkmode.html" .}} +
+

404.

+

Hey! You look a little lost. This page doesn't exist (or may be private).

+ ↳ Let's get you home. +
+
+ + + diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html index 1ae86075c..ff4e8b39c 100644 --- a/layouts/_default/_markup/render-image.html +++ b/layouts/_default/_markup/render-image.html @@ -1,8 +1,8 @@ -{{$src := .Destination | safeURL }} -{{$external := strings.HasPrefix $src "http" }} -{{- if $external -}} -{{ .Text }} -{{- else -}} -{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}} -{{ .Text }} -{{- end -}} +{{$src := .Destination | safeURL }} +{{$external := strings.HasPrefix $src "http" }} +{{- if $external -}} +{{ .Text }} +{{- else -}} +{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}} +{{ .Text }} +{{- end -}} diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html index 0cb588371..4757b7284 100644 --- a/layouts/_default/_markup/render-link.html +++ b/layouts/_default/_markup/render-link.html @@ -1,16 +1,16 @@ -{{$trimmed := strings.TrimSuffix ".md" (.Destination | safeURL)}} -{{$dashedurl := replace $trimmed "%20" "-" }} -{{$external := strings.HasPrefix $dashedurl "http" }} -{{- if $external -}} -{{ .Text | safeHTML }} -{{- else -}} -{{$spacedurl := replace $trimmed "%20" " " }} -{{$fixedUrl := (cond (hasPrefix $spacedurl "/") $spacedurl (print "/" $spacedurl)) | urlize}} -{{$nonexistent := eq (.Page.GetPage $spacedurl).RelPermalink ""}} -{{$rooted := default $spacedurl (strings.TrimRight "/" (.Page.GetPage $spacedurl).RelPermalink) }} -{{- .Text | safeHTML -}} - -{{- end -}} +{{$trimmed := strings.TrimSuffix ".md" (.Destination | safeURL)}} +{{$dashedurl := replace $trimmed "%20" "-" }} +{{$external := strings.HasPrefix $dashedurl "http" }} +{{- if $external -}} +{{ .Text | safeHTML }} +{{- else -}} +{{$spacedurl := replace $trimmed "%20" " " }} +{{$fixedUrl := (cond (hasPrefix $spacedurl "/") $spacedurl (print "/" $spacedurl)) | urlize}} +{{$nonexistent := eq (.Page.GetPage $spacedurl).RelPermalink ""}} +{{$rooted := default $spacedurl ((.Page.GetPage $spacedurl).RelPermalink) }} +{{- .Text | safeHTML -}} + +{{- end -}} diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 90d44cda6..ccb3b93ac 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -1,10 +1,10 @@ - - -{{ block "head" . }} -{{ end }} - - -{{ block "main" . }} -{{ end }} - + + +{{ block "head" . }} +{{ end }} + + +{{ block "main" . }} +{{ end }} + \ No newline at end of file diff --git a/layouts/_default/section.html b/layouts/_default/section.html index 783aa2332..abdf0b05c 100644 --- a/layouts/_default/section.html +++ b/layouts/_default/section.html @@ -1,25 +1,24 @@ - - -{{ partial "head.html" . }} - - -{{partial "search.html" .}} -
- -
-

{{ .Site.Data.config.page_title }}

- Search IconIcon to open search -
- {{partial "darkmode.html" .}} -
-
-

All {{.Title}}

- {{partial "page-list.html" .Paginator.Pages.ByLastmod.Reverse }} - {{ template "_internal/pagination.html" .}} -
- {{partial "contact.html" .}} -
-{{partial "popover.html" .}} - - - + + +{{ partial "head.html" . }} + + +{{partial "search.html" .}} +
+ +
+

{{ .Site.Data.config.page_title }}

+ Search IconIcon to open search +
+ {{partial "darkmode.html" .}} +
+
+

All {{.Title}}

+ {{partial "page-list.html" .Paginator.Pages.ByLastmod.Reverse }} + {{ template "_internal/pagination.html" .}} +
+ {{partial "contact.html" .}} +
+ + + diff --git a/layouts/_default/single.html b/layouts/_default/single.html index cfaea7100..91eda290b 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -1,33 +1,32 @@ - - -{{ partial "head.html" . }} - - -{{partial "search.html" .}} -
- -
-

{{ .Site.Data.config.page_title }}

- Search IconIcon to open search -
- {{partial "darkmode.html" .}} -
-
- {{if .Title}}

{{ .Title }}

{{end}} -

- Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} -

- - {{partial "toc.html" .}} - {{partial "textprocessing.html" . }} -
- {{partial "footer.html" .}} - {{partial "popover.html" .}} -
- - - + + +{{ partial "head.html" . }} + + +{{partial "search.html" .}} +
+ +
+

{{ .Site.Data.config.page_title }}

+ Search IconIcon to open search +
+ {{partial "darkmode.html" .}} +
+
+ {{if .Title}}

{{ .Title }}

{{end}} +

+ Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} +

+ + {{partial "toc.html" .}} + {{partial "textprocessing.html" . }} +
+ {{partial "footer.html" .}} +
+ + + diff --git a/layouts/_default/taxonomy.html b/layouts/_default/taxonomy.html index be7532237..e0a1e876c 100644 --- a/layouts/_default/taxonomy.html +++ b/layouts/_default/taxonomy.html @@ -1,34 +1,33 @@ - - -{{ partial "head.html" . }} - - -{{partial "search.html" .}} -
- -
-

{{ .Site.Data.config.page_title }}

- Search IconIcon to open search -
- {{partial "darkmode.html" .}} -
-
-

All {{.Title}}

-
- {{ range .Site.Taxonomies.tags.ByCount }} -
-

{{ .Page.Title }}

-

{{ .Count }} notes with this tag {{if gt .Count 2}}(showing first 2 results){{end}}

-
- {{ with ($.Site.GetPage (printf "/tags/%s" .Page.Title)) }} - {{partial "page-list.html" (first 2 .Pages.ByLastmod.Reverse)}} - {{ end }} - {{ end }} -
-
- {{partial "contact.html" .}} -
-{{partial "popover.html" .}} - - - + + +{{ partial "head.html" . }} + + +{{partial "search.html" .}} +
+ +
+

{{ .Site.Data.config.page_title }}

+ Search IconIcon to open search +
+ {{partial "darkmode.html" .}} +
+
+

All {{.Title}}

+
+ {{ range .Site.Taxonomies.tags.ByCount }} +
+

{{ .Page.Title }}

+

{{ .Count }} notes with this tag {{if gt .Count 2}}(showing first 2 results){{end}}

+
+ {{ with ($.Site.GetPage (printf "/tags/%s" .Page.Title)) }} + {{partial "page-list.html" (first 2 .Pages.ByLastmod.Reverse)}} + {{ end }} + {{ end }} +
+
+ {{partial "contact.html" .}} +
+ + + diff --git a/layouts/_default/term.html b/layouts/_default/term.html index 7b897ec34..58f024bcc 100644 --- a/layouts/_default/term.html +++ b/layouts/_default/term.html @@ -1,25 +1,24 @@ - - -{{ partial "head.html" . }} - - -{{partial "search.html" .}} -
- -
-

{{ .Site.Data.config.page_title }}

- Search IconIcon to open search -
- {{partial "darkmode.html" .}} -
-
-

Tag: {{.Title | humanize}}

- {{partial "page-list.html" .Paginator.Pages}} - {{ template "_internal/pagination.html" . }} -
- {{partial "contact.html" .}} -
-{{partial "popover.html" .}} - - - + + +{{ partial "head.html" . }} + + +{{partial "search.html" .}} +
+ +
+

{{ .Site.Data.config.page_title }}

+ Search IconIcon to open search +
+ {{partial "darkmode.html" .}} +
+
+

Tag: {{.Title | humanize}}

+ {{partial "page-list.html" .Paginator.Pages}} + {{ template "_internal/pagination.html" . }} +
+ {{partial "contact.html" .}} +
+ + + diff --git a/layouts/index.html b/layouts/index.html index cffe3b21c..224c99784 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -1,24 +1,22 @@ - - -{{ partial "head.html" . }} - - -{{partial "search.html" .}} -
- -
-

{{if .Title}}{{ .Title }}{{else}}Untitled{{end}}

- Search IconIcon to open search -
- {{partial "darkmode.html" .}} -
-
- {{partial "toc.html" .}} - {{partial "textprocessing.html" . }} -
- {{partial "footer.html" .}} - {{partial "popover.html" .}} -
- - - + + +{{ partial "head.html" . }} + + +{{partial "search.html" .}} +
+ +
+

{{if .Title}}{{ .Title }}{{else}}Untitled{{end}}

+ Search IconIcon to open search +
+ {{partial "darkmode.html" .}} +
+
+ {{partial "toc.html" .}} + {{partial "textprocessing.html" . }} +
+ {{partial "footer.html" .}} +
+ + diff --git a/layouts/partials/backlinks.html b/layouts/partials/backlinks.html index 45187b575..23c9091a5 100644 --- a/layouts/partials/backlinks.html +++ b/layouts/partials/backlinks.html @@ -1,24 +1,30 @@ -

Backlinks

- +

Backlinks

+ diff --git a/layouts/partials/contact.html b/layouts/partials/contact.html index 9eaa87faa..7fb991e9d 100644 --- a/layouts/partials/contact.html +++ b/layouts/partials/contact.html @@ -1,14 +1,14 @@ - -
-
-

Made by {{ $.Site.Data.config.name }} using Quartz, © {{ dateFormat "2006" now }}

-
    - {{ if not .IsHome }} -
  • Home
  • - {{end}} - {{- range $.Site.Data.config.links -}} -
  • {{.link_name}}
  • - {{- end -}} -
-
-
+ +
+
+

Made by {{ $.Site.Data.config.name }} using Quartz, © {{ dateFormat "2006" now }}

+
    + {{ if not .IsHome }} +
  • Home
  • + {{end}} + {{- range $.Site.Data.config.links -}} +
  • {{.link_name}}
  • + {{- end -}} +
+
+
diff --git a/layouts/partials/darkmode.html b/layouts/partials/darkmode.html index 3dec38f1e..d7540c22a 100644 --- a/layouts/partials/darkmode.html +++ b/layouts/partials/darkmode.html @@ -1,15 +1,15 @@ -
- - - +
+ + +
\ No newline at end of file diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html index cc891362c..6d4ef17b9 100644 --- a/layouts/partials/footer.html +++ b/layouts/partials/footer.html @@ -1,11 +1,11 @@ -
-
- -
- {{partial "graph.html" .}} -
-
- +
+
+ +
+ {{partial "graph.html" .}} +
+
+ {{partial "contact.html" .}} \ No newline at end of file diff --git a/layouts/partials/graph.html b/layouts/partials/graph.html index 6be8c0651..b9f79763c 100644 --- a/layouts/partials/graph.html +++ b/layouts/partials/graph.html @@ -1,25 +1,18 @@ - -

Interactive Graph

-
- -{{ $js := resources.Get "js/graph.js" | resources.Fingerprint "md5" }} - - + +

Interactive Graph

+
+ +{{ $js := resources.Get "js/graph.js" | resources.Fingerprint "md5" }} + diff --git a/layouts/partials/head.html b/layouts/partials/head.html index d3be2a606..a5f23da30 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -1,46 +1,110 @@ - - - - - {{ if .Title }}{{ .Title }}{{ else }}{{ $.Site.Data.config.page_title }}{{ end }} - - - - - - {{$sass := resources.Match "styles/[!_]*.scss" }} - {{$css := slice }} - {{range $sass}} - {{$scss := . | resources.ToCSS (dict "outputStyle" "compressed") }} - {{$css = $css | append $scss}} - {{end}} - {{$finalCss := $css | resources.Concat "styles.css" | resources.Fingerprint "md5" | resources.Minify }} - - - {{ $darkMode := resources.Get "js/darkmode.js" | resources.Fingerprint "md5" | resources.Minify }} - - {{partial "katex.html" .}} - - - {{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint "md5" | resources.Minify | }} - {{$contentIndex := resources.Get "indices/contentIndex.json" | resources.Fingerprint "md5" | resources.Minify }} - - -{{ template "_internal/google_analytics.html" . }} + + + + + + {{ if .Title }}{{ .Title }}{{ else }}{{ $.Site.Data.config.page_title }}{{ + end }} + + + + + + + {{$sass := resources.Match "styles/[!_]*.scss" }} + {{$css := slice }} + {{range $sass}} + {{$scss := . | resources.ToCSS (dict "outputStyle" "compressed") }} + {{$css = $css | append $scss}} + {{end}} + {{$finalCss := $css | resources.Concat "styles.css" | resources.Fingerprint "md5" | resources.Minify }} + + + {{ $darkMode := resources.Get "js/darkmode.js" | resources.Fingerprint "md5" | + resources.Minify }} + + {{partial "katex.html" .}} + + {{ $popover := resources.Get "js/popover.js" | resources.Fingerprint "md5" | + resources.Minify }} + + + + {{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint + "md5" | resources.Minify | }} {{$contentIndex := resources.Get + "indices/contentIndex.json" | resources.Fingerprint "md5" | resources.Minify + }} + + {{if $.Site.Data.config.enableSPA}} + {{ $router := resources.Get "js/router.js" | resources.Fingerprint "md5" | + resources.Minify }} + + {{else}} + + {{end}} + +{{ template "_internal/google_analytics.html" . }} diff --git a/layouts/partials/katex.html b/layouts/partials/katex.html index 06ef1c969..756ef779e 100644 --- a/layouts/partials/katex.html +++ b/layouts/partials/katex.html @@ -1,16 +1,5 @@ -{{if $.Site.Data.config.enableLatex}} - - - - -{{end}} +{{if $.Site.Data.config.enableLatex}} + + + +{{end}} diff --git a/layouts/partials/page-list.html b/layouts/partials/page-list.html index e2c01e315..6c2249baf 100644 --- a/layouts/partials/page-list.html +++ b/layouts/partials/page-list.html @@ -1,15 +1,15 @@ -
    - {{- range . -}} -
  • -
    -
    -

    {{- .Title -}}

    -

    {{- .Summary -}}{{if .Truncated}}...{{end}}

    -
    -

    - {{ .ReadingTime }} minute read. Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} -

    -
    -
  • - {{- end -}} -
\ No newline at end of file +
    + {{- range . -}} +
  • +
    +
    +

    {{- .Title -}}

    +

    {{- .Summary -}}{{if .Truncated}}...{{end}}

    +
    +

    + {{ .ReadingTime }} minute read. Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} +

    +
    +
  • + {{- end -}} +
diff --git a/layouts/partials/popover.html b/layouts/partials/popover.html deleted file mode 100644 index 4d35c0f38..000000000 --- a/layouts/partials/popover.html +++ /dev/null @@ -1,7 +0,0 @@ -{{if $.Site.Data.config.enableLinkPreview}} -{{ $js := resources.Get "js/popover.js" | resources.Fingerprint "md5" | resources.Minify }} - - -{{end}} \ No newline at end of file diff --git a/layouts/partials/search.html b/layouts/partials/search.html index bee0ba687..f727184a6 100644 --- a/layouts/partials/search.html +++ b/layouts/partials/search.html @@ -1,10 +1,10 @@ -
-
- -
-
-
-
- -{{ $js := resources.Get "js/search.js" | resources.Fingerprint "md5" | resources.Minify }} - +
+
+ +
+
+
+
+ +{{ $js := resources.Get "js/search.js" | resources.Fingerprint "md5" | resources.Minify }} + diff --git a/layouts/partials/textprocessing.html b/layouts/partials/textprocessing.html index 868d626ac..8e8c99917 100644 --- a/layouts/partials/textprocessing.html +++ b/layouts/partials/textprocessing.html @@ -1,56 +1,60 @@ -{{ $content := .Content }} -{{ $raw := .RawContent }} -{{ $page := .Page }} - -{{/* Escape slashes for Latex to fix line breaks */}} -{{$latex := findRE "\\$\\$([^\\$]+)\\$\\$" $content}} -{{range $latex}} - {{$fixed := replaceRE "\\\\(?: +|\\n)" "\\\\" .}} - {{$content = replace $content . $fixed}} -{{end}} - -{{/* Wikilinks */}} -{{$wikilinks := $content | findRE "!?\\[\\[\\S[^\\[\\]\\|]*(?:\\|[^\\[\\]]*)?\\S\\]\\]" }} -{{$codefences := $raw | findRE "\\x60[^\\x60\\n]+\\x60"}} -{{$codeblocks := $raw | findRE "\\x60{3}[^\\x60]+\\x60{3}"}} -{{$code := union $codefences $codeblocks}} -{{range $wikilinks}} - {{$cur := .}} - {{$incode := false}} - {{range $code}} - {{if (in . $cur)}} - {{$incode = true}} - {{end}} - {{end}} - {{if not $incode}} - {{if (hasPrefix . "!")}} - {{$inner := . | strings.TrimPrefix "![[" | strings.TrimSuffix "]]" }} - {{$split := split $inner "|"}} - {{$path := index $split 0 | relURL}} - {{$reference := split $path "#"}} - {{$title := index $reference 0}} - {{$display := default $title (index $split 1)}} - {{$img := printf "" $path $display}} - {{$content = replace $content . $img}} - {{else}} - {{$inner := . | strings.TrimPrefix "[[" | strings.TrimSuffix "]]" }} - {{$split := split $inner "|"}} - {{$path := index $split 0}} - {{$reference := split $path "#"}} - {{$title := index $reference 0}} - {{$block := default "" (index $reference 1)}} - {{$block = strings.TrimRight "/" (cond (eq $block "") $block (printf "#%s" $block))}} - {{$href := strings.TrimRight "/" ($page.GetPage $title).RelPermalink}} - {{$display := default $title (index $split 1)}} - {{if not $href}} - {{$link := printf "%s" $display}} - {{$content = replace $content . $link}} - {{else}} - {{$fullhref := printf "%s%s" $href $block }} - {{$link := printf "%s" $fullhref $href $display}} - {{$content = replace $content . $link}} - {{end}} - {{end}} - {{end}} -{{end}} -{{ $content | safeHTML }} +{{ $content := .Content }} +{{ $raw := .RawContent }} +{{ $page := .Page }} + +{{/* Escape slashes for Latex to fix line breaks */}} +{{$latex := findRE "\\$\\$([^\\$]+)\\$\\$" $content}} +{{range $latex}} + {{$fixed := replaceRE "\\\\(?: +|\\n)" "\\\\" .}} + {{$content = replace $content . $fixed}} +{{end}} + +{{/* Wikilinks */}} +{{$wikilinks := $content | findRE "!?\\[\\[\\S[^\\[\\]\\|]*(?:\\|[^\\[\\]]*)?\\S\\]\\]" }} +{{$codefences := $raw | findRE "\\x60[^\\x60\\n]+\\x60"}} +{{$codeblocks := $raw | findRE "\\x60{3}[^\\x60]+\\x60{3}"}} +{{$code := union $codefences $codeblocks}} +{{range $wikilinks}} + {{$cur := .}} + {{$incode := false}} + {{range $code}} + {{if (in . $cur)}} + {{$incode = true}} + {{end}} + {{end}} + {{if not $incode}} + {{if (hasPrefix . "!")}} + {{$inner := . | strings.TrimPrefix "![[" | strings.TrimSuffix "]]" }} + {{$split := split $inner "|"}} + {{$path := index $split 0 | relURL}} + {{$reference := split $path "#"}} + {{$title := index $reference 0}} + {{$display := default $title (index $split 1)}} + {{$img := printf "" $path $display}} + {{$content = replace $content . $img}} + {{else}} + {{$inner := . | strings.TrimPrefix "[[" | strings.TrimSuffix "]]" }} + {{$split := split $inner "|"}} + {{$path := index $split 0}} + {{$reference := split $path "#"}} + {{$title := index $reference 0}} + {{$block := default "" (index $reference 1)}} + {{$block = strings.TrimRight "/" (cond (eq $block "") $block (printf "#%s" $block))}} + {{$href := strings.TrimRight "/" ($page.GetPage $title).RelPermalink}} + {{$display := default $title (index $split 1)}} + {{if not $href}} + {{$link := printf "%s" $display}} + {{$content = replace $content . $link}} + {{else}} + {{$fullhref := printf "%s%s" $href $block }} + {{$link := printf "%s" $fullhref $href $display}} + {{$content = replace $content . $link}} + {{end}} + {{end}} + {{end}} +{{end}} + +{{/* Add copyable anchors */}} +{{ $content = $content | replaceRE "()(.+)()" `${1}# ${3}${4}` }} + +{{ $content | safeHTML }} diff --git a/layouts/partials/toc.html b/layouts/partials/toc.html index a3b1ec0ec..acda23d6d 100644 --- a/layouts/partials/toc.html +++ b/layouts/partials/toc.html @@ -1,8 +1,8 @@ -{{ if (and $.Site.Data.config.enableToc (ne .Params.enableToc false) (gt .WordCount 250)) }} - -{{end}} +{{ if (and $.Site.Data.config.enableToc (ne .Params.enableToc false) (gt .WordCount 250)) }} + +{{end}}