updated base quartz

This commit is contained in:
Jet Hughes 2022-05-07 21:44:30 +12:00
parent 4ba7f82592
commit bf3e060a13
33 changed files with 1842 additions and 1648 deletions

2
.github/FUNDING.yml vendored
View File

@ -1 +1 @@
github: [jackyzha0] github: [jackyzha0]

View File

@ -1,32 +1,32 @@
--- ---
name: Bug report name: Bug report
about: Something about Quartz isn't working the way you expect about: Something about Quartz isn't working the way you expect
title: '' title: ''
labels: bug labels: bug
assignees: '' assignees: ''
--- ---
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is.
**To Reproduce** **To Reproduce**
Steps to reproduce the behavior: Steps to reproduce the behavior:
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
**Expected behavior** **Expected behavior**
A clear and concise description of what you expected to happen. A clear and concise description of what you expected to happen.
**Screenshots** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):** **Desktop (please complete the following information):**
- Device: [e.g. iPhone6] - Device: [e.g. iPhone6]
- OS: [e.g. iOS] - OS: [e.g. iOS]
- Browser [e.g. chrome, safari] - Browser [e.g. chrome, safari]
**Additional context** **Additional context**
Add any other context about the problem here. Add any other context about the problem here.

View File

@ -1,20 +1,20 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea or improvement for Quartz about: Suggest an idea or improvement for Quartz
title: '' title: ''
labels: enhancement labels: enhancement
assignees: '' assignees: ''
--- ---
**Is your feature request related to a problem? Please describe.** **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 [...] A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like** **Describe the solution you'd like**
A clear and concise description of what you want to happen. A clear and concise description of what you want to happen.
**Describe alternatives you've considered** **Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered. A clear and concise description of any alternative solutions or features you've considered.
**Additional context** **Additional context**
Add any other context or screenshots about the feature request here. Add any other context or screenshots about the feature request here.

View File

@ -1,18 +1,20 @@
.DEFAULT_GOAL := serve .DEFAULT_GOAL := serve
help: ## Show all Makefile targets 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}' @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 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) go install github.com/jackyzha0/hugo-obsidian@latest
git fetch upstream @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git)
git log --oneline --decorate --graph ..upstream/hugo git fetch upstream
git checkout -p upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data 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) update-force: ## Forcefully pull all changes and don't ask to patch
git fetch upstream go install github.com/jackyzha0/hugo-obsidian@latest
git checkout upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data @git remote show upstream || (echo "remote 'upstream' not present, setting 'upstream'" && git remote add upstream https://github.com/jackyzha0/quartz.git)
git fetch upstream
serve: ## Serve Quartz locally git checkout upstream/hugo -- layouts .github Makefile assets/js assets/styles/base.scss assets/styles/darkmode.scss config.toml data
hugo-obsidian -input=content -output=assets/indices -index -root=. && hugo server --enableGitInfo
serve: ## Serve Quartz locally
hugo-obsidian -input=content -output=assets/indices -index -root=. && hugo server --enableGitInfo

View File

@ -1,29 +1,29 @@
const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark' const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
const currentTheme = localStorage.getItem('theme') ?? userPref const currentTheme = localStorage.getItem('theme') ?? userPref
if (currentTheme) { if (currentTheme) {
document.documentElement.setAttribute('saved-theme', currentTheme); document.documentElement.setAttribute('saved-theme', currentTheme);
} }
const switchTheme = (e) => { const switchTheme = (e) => {
if (e.target.checked) { if (e.target.checked) {
document.documentElement.setAttribute('saved-theme', 'dark') document.documentElement.setAttribute('saved-theme', 'dark')
localStorage.setItem('theme', 'dark') localStorage.setItem('theme', 'dark')
} }
else { else {
document.documentElement.setAttribute('saved-theme', 'light') document.documentElement.setAttribute('saved-theme', 'light')
localStorage.setItem('theme', 'light') localStorage.setItem('theme', 'light')
} }
} }
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
// Darkmode toggle // Darkmode toggle
const toggleSwitch = document.querySelector('#darkmode-toggle') const toggleSwitch = document.querySelector('#darkmode-toggle')
// listen for toggle // listen for toggle
toggleSwitch.addEventListener('change', switchTheme, false) toggleSwitch.addEventListener('change', switchTheme, false)
if (currentTheme === 'dark') { if (currentTheme === 'dark') {
toggleSwitch.checked = true toggleSwitch.checked = true
} }
}) })

View File

@ -1,220 +1,283 @@
async function drawGraph(url, baseUrl, pathColors, depth, enableDrag, enableLegend, enableZoom) { async function drawGraph(
const { index, links, content } = await fetchData baseUrl,
const curPage = url.replace(baseUrl, "") pathColors,
depth,
const parseIdsFromLinks = (links) => [...(new Set(links.flatMap(link => ([link.source, link.target]))))] enableDrag,
enableLegend,
const neighbours = new Set() enableZoom
const wl = [curPage || "/", "__SENTINEL"] ) {
if (depth >= 0) { const container = document.getElementById('graph-container')
while (depth >= 0 && wl.length > 0) { const { index, links, content } = await fetchData
// compute neighbours
const cur = wl.shift() // Use .pathname to remove hashes / searchParams / text fragments
if (cur === "__SENTINEL") { const cleanUrl = window.location.origin + window.location.pathname
depth--
wl.push("__SENTINEL") const curPage = cleanUrl.replace(/\/$/g, "").replace(baseUrl, "")
} else {
neighbours.add(cur) const parseIdsFromLinks = (links) => [
const outgoing = index.links[cur] || [] ...new Set(links.flatMap((link) => [link.source, link.target])),
const incoming = index.backlinks[cur] || [] ]
wl.push(...outgoing.map(l => l.target), ...incoming.map(l => l.source))
} // 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
} else { const copyLinks = JSON.parse(JSON.stringify(links))
parseIdsFromLinks(links).forEach(id => neighbours.add(id))
} const neighbours = new Set()
const wl = [curPage || '/', '__SENTINEL']
const data = { if (depth >= 0) {
nodes: [...neighbours].map(id => ({ id })), while (depth >= 0 && wl.length > 0) {
links: links.filter(l => neighbours.has(l.source) && neighbours.has(l.target)), // compute neighbours
} const cur = wl.shift()
if (cur === '__SENTINEL') {
const color = (d) => { depth--
if (d.id === curPage || (d.id === "/" && curPage === "")) { wl.push('__SENTINEL')
return "var(--g-node-active)" } else {
} neighbours.add(cur)
const outgoing = index.links[cur] || []
for (const pathColor of pathColors) { const incoming = index.backlinks[cur] || []
const path = Object.keys(pathColor)[0] wl.push(
const colour = pathColor[path] ...outgoing.map((l) => l.target),
if (d.id.startsWith(path)) { ...incoming.map((l) => l.source)
return colour )
} }
} }
} else {
return "var(--g-node)" parseIdsFromLinks(copyLinks).forEach((id) => neighbours.add(id))
} }
const drag = simulation => { const data = {
function dragstarted(event, d) { nodes: [...neighbours].map((id) => ({ id })),
if (!event.active) simulation.alphaTarget(1).restart(); links: copyLinks.filter(
d.fx = d.x; (l) => neighbours.has(l.source) && neighbours.has(l.target)
d.fy = d.y; ),
} }
function dragged(event, d) { const color = (d) => {
d.fx = event.x; if (d.id === curPage || (d.id === '/' && curPage === '')) {
d.fy = event.y; return 'var(--g-node-active)'
} }
function dragended(event, d) { for (const pathColor of pathColors) {
if (!event.active) simulation.alphaTarget(0); const path = Object.keys(pathColor)[0]
d.fx = null; const colour = pathColor[path]
d.fy = null; if (d.id.startsWith(path)) {
} return colour
}
const noop = () => { } }
return d3.drag()
.on("start", enableDrag ? dragstarted : noop) return 'var(--g-node)'
.on("drag", enableDrag ? dragged : noop) }
.on("end", enableDrag ? dragended : noop);
} const drag = (simulation) => {
function dragstarted(event, d) {
const height = 250 if (!event.active) simulation.alphaTarget(1).restart()
const width = document.getElementById("graph-container").offsetWidth d.fx = d.x
d.fy = d.y
const simulation = d3.forceSimulation(data.nodes) }
.force("charge", d3.forceManyBody().strength(-30))
.force("link", d3.forceLink(data.links).id(d => d.id)) function dragged(event, d) {
.force("center", d3.forceCenter()); d.fx = event.x
d.fy = event.y
const svg = d3.select('#graph-container') }
.append('svg')
.attr('width', width) function dragended(event, d) {
.attr('height', height) if (!event.active) simulation.alphaTarget(0)
.attr("viewBox", [-width / 2, -height / 2, width, height]); d.fx = null
d.fy = null
if (enableLegend) { }
const legend = [
{ "Current": "var(--g-node-active)" }, const noop = () => { }
{ "Note": "var(--g-node)" }, return d3
...pathColors .drag()
] .on('start', enableDrag ? dragstarted : noop)
legend.forEach((legendEntry, i) => { .on('drag', enableDrag ? dragged : noop)
const key = Object.keys(legendEntry)[0] .on('end', enableDrag ? dragended : noop)
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") const height = Math.max(container.offsetHeight, 250)
}) const width = container.offsetWidth
}
const simulation = d3
// draw links between nodes .forceSimulation(data.nodes)
const link = svg.append("g") .force('charge', d3.forceManyBody().strength(-30))
.selectAll("line") .force(
.data(data.links) 'link',
.join("line") d3
.attr("class", "link") .forceLink(data.links)
.attr("stroke", "var(--g-link)") .id((d) => d.id)
.attr("stroke-width", 2) .distance(40)
.attr("data-source", d => d.source.id) )
.attr("data-target", d => d.target.id) .force('center', d3.forceCenter())
// svg groups const svg = d3
const graphNode = svg.append("g") .select('#graph-container')
.selectAll("g") .append('svg')
.data(data.nodes) .attr('width', width)
.enter().append("g") .attr('height', height)
.attr('viewBox', [-width / 2, -height / 2, width, height])
// draw individual nodes
const node = graphNode.append("circle") if (enableLegend) {
.attr("class", "node") const legend = [
.attr("id", (d) => d.id) { Current: 'var(--g-node-active)' },
.attr("r", (d) => { { Note: 'var(--g-node)' },
const numOut = index.links[d.id]?.length || 0 ...pathColors,
const numIn = index.backlinks[d.id]?.length || 0 ]
return 3 + (numOut + numIn) / 4 legend.forEach((legendEntry, i) => {
}) const key = Object.keys(legendEntry)[0]
.attr("fill", color) const colour = legendEntry[key]
.style("cursor", "pointer") svg
.on("click", (_, d) => { .append('circle')
window.location.href = baseUrl + '/' + decodeURI(d.id).replace(/\s+/g, '-') .attr('cx', -width / 2 + 20)
}) .attr('cy', height / 2 - 30 * (i + 1))
.on("mouseover", function(_, d) { .attr('r', 6)
d3.selectAll(".node") .style('fill', colour)
.transition() svg
.duration(100) .append('text')
.attr("fill", "var(--g-node-inactive)") .attr('x', -width / 2 + 40)
.attr('y', height / 2 - 30 * (i + 1))
const neighbours = parseIdsFromLinks([...(index.links[d.id] || []), ...(index.backlinks[d.id] || [])]) .text(key)
const neighbourNodes = d3.selectAll(".node").filter(d => neighbours.includes(d.id)) .style('font-size', '15px')
const currentId = d.id .attr('alignment-baseline', 'middle')
const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) })
}
// highlight neighbour nodes
neighbourNodes // draw links between nodes
.transition() const link = svg
.duration(200) .append('g')
.attr("fill", color) .selectAll('line')
.data(data.links)
// highlight links .join('line')
linkNodes .attr('class', 'link')
.transition() .attr('stroke', 'var(--g-link)')
.duration(200) .attr('stroke-width', 2)
.attr("stroke", "var(--g-link-active)") .attr('data-source', (d) => d.source.id)
.attr('data-target', (d) => d.target.id)
// show text for self
d3.select(this.parentNode) // svg groups
.select("text") const graphNode = svg
.raise() .append('g')
.transition() .selectAll('g')
.duration(200) .data(data.nodes)
.style("opacity", 1) .enter()
}).on("mouseleave", function(_, d) { .append('g')
d3.selectAll(".node")
.transition() // calculate radius
.duration(200) const nodeRadius = (d) => {
.attr("fill", color) const numOut = index.links[d.id]?.length || 0
const numIn = index.backlinks[d.id]?.length || 0
const currentId = d.id return 3 + (numOut + numIn) / 4
const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId) }
linkNodes // draw individual nodes
.transition() const node = graphNode
.duration(200) .append('circle')
.attr("stroke", "var(--g-link)") .attr('class', 'node')
.attr('id', (d) => d.id)
d3.select(this.parentNode) .attr('r', nodeRadius)
.select("text") .attr('fill', color)
.transition() .style('cursor', 'pointer')
.duration(200) .on('click', (_, d) => {
.style("opacity", 0) // SPA navigation
}) window.navigate(
.call(drag(simulation)); new URL(`${baseUrl}${decodeURI(d.id).replace(/\s+/g, '-')}/`),
'.singlePage'
// draw labels )
const labels = graphNode.append("text") })
.attr("dx", 12) .on('mouseover', function(_, d) {
.attr("dy", ".35em") d3.selectAll('.node')
.text((d) => content[d.id]?.title || d.id.replace("-", " ")) .transition()
.style("opacity", 0) .duration(100)
.style("pointer-events", "none") .attr('fill', 'var(--g-node-inactive)')
.call(drag(simulation));
const neighbours = parseIdsFromLinks([
// set panning ...(index.links[d.id] || []),
...(index.backlinks[d.id] || []),
if (enableZoom) { ])
svg.call(d3.zoom() const neighbourNodes = d3
.extent([[0, 0], [width, height]]) .selectAll('.node')
.scaleExtent([0.25, 4]) .filter((d) => neighbours.includes(d.id))
.on("zoom", ({ transform }) => { const currentId = d.id
link.attr("transform", transform); const linkNodes = d3
node.attr("transform", transform); .selectAll('.link')
labels.attr("transform", transform); .filter((d) => d.source.id === currentId || d.target.id === currentId)
}));
} // highlight neighbour nodes
neighbourNodes.transition().duration(200).attr('fill', color)
// progress the simulation
simulation.on("tick", () => { // highlight links
link linkNodes
.attr("x1", d => d.source.x) .transition()
.attr("y1", d => d.source.y) .duration(200)
.attr("x2", d => d.target.x) .attr('stroke', 'var(--g-link-active)')
.attr("y2", d => d.target.y)
node // show text for self
.attr("cx", d => d.x) d3.select(this.parentNode)
.attr("cy", d => d.y) .raise()
labels .select('text')
.attr("x", d => d.x) .transition()
.attr("y", d => d.y) .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)
})
}

View File

@ -1,35 +1,58 @@
function htmlToElement(html) { function htmlToElement(html) {
const template = document.createElement('template') const template = document.createElement("template")
html = html.trim() html = html.trim()
template.innerHTML = html template.innerHTML = html
return template.content.firstChild return template.content.firstChild
} }
function initPopover(baseURL) { function initPopover(baseURL, useContextualBacklinks, renderLatex) {
const basePath = baseURL.replace(window.location.origin, "") const basePath = baseURL.replace(window.location.origin, "")
document.addEventListener("DOMContentLoaded", () => { fetchData.then(({ content }) => {
fetchData.then(({ content }) => { const links = [...document.getElementsByClassName("internal-link")]
const links = [...document.getElementsByClassName("internal-link")] links
links .filter(li => li.dataset.src || (li.dataset.idx && useContextualBacklinks))
.filter(li => li.dataset.src) .forEach(li => {
.forEach(li => { var el
const linkDest = content[li.dataset.src.replace(basePath, "")] if (li.dataset.ctx) {
if (linkDest) { const linkDest = content[li.dataset.src]
const popoverElement = `<div class="popover"> const popoverElement = `<div class="popover">
<h3>${linkDest.title}</h3> <h3>${linkDest.title}</h3>
<p>${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...</p> <p>${highlight(removeMarkdown(linkDest.content), li.dataset.ctx)}...</p>
<p class="meta">${new Date(linkDest.lastmodified).toLocaleDateString()}</p> <p class="meta">${new Date(linkDest.lastmodified).toLocaleDateString()}</p>
</div>` </div>`
const el = htmlToElement(popoverElement) el = htmlToElement(popoverElement)
li.appendChild(el) } else {
li.addEventListener("mouseover", () => { const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")]
el.classList.add("visible") if (linkDest) {
}) const popoverElement = `<div class="popover">
li.addEventListener("mouseout", () => { <h3>${linkDest.title}</h3>
el.classList.remove("visible") <p>${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...</p>
}) <p class="meta">${new Date(linkDest.lastmodified).toLocaleDateString()}</p>
} </div>`
}) 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")
})
}
})
})
}

12
assets/js/router.js Normal file
View File

@ -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))
}

View File

@ -1,239 +1,270 @@
// code from https://github.com/danestves/markdown-to-text // code from https://github.com/danestves/markdown-to-text
const removeMarkdown = ( const removeMarkdown = (
markdown, markdown,
options = { options = {
listUnicodeChar: false, listUnicodeChar: false,
stripListLeaders: true, stripListLeaders: true,
gfm: true, gfm: true,
useImgAltText: false, useImgAltText: false,
preserveLinks: false, preserveLinks: false,
} }
) => { ) => {
let output = markdown || ""; let output = markdown || ''
output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, ""); output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, '')
try { try {
if (options.stripListLeaders) { if (options.stripListLeaders) {
if (options.listUnicodeChar) if (options.listUnicodeChar)
output = output.replace( output = output.replace(
/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, /^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm,
options.listUnicodeChar + " $1" options.listUnicodeChar + ' $1'
); )
else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, "$1"); else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, '$1')
} }
if (options.gfm) { if (options.gfm) {
output = output output = output
.replace(/\n={2,}/g, "\n") .replace(/\n={2,}/g, '\n')
.replace(/~{3}.*\n/g, "") .replace(/~{3}.*\n/g, '')
.replace(/~~/g, "") .replace(/~~/g, '')
.replace(/`{3}.*\n/g, ""); .replace(/`{3}.*\n/g, '')
} }
if (options.preserveLinks) { if (options.preserveLinks) {
output = output.replace(/\[(.*?)\][\[\(](.*?)[\]\)]/g, "$1 ($2)") output = output.replace(/\[(.*?)\][\[\(](.*?)[\]\)]/g, '$1 ($2)')
} }
output = output output = output
.replace(/<[^>]*>/g, "") .replace(/<[^>]*>/g, '')
.replace(/^[=\-]{2,}\s*$/g, "") .replace(/^[=\-]{2,}\s*$/g, '')
.replace(/\[\^.+?\](\: .*?$)?/g, "") .replace(/\[\^.+?\](\: .*?$)?/g, '')
.replace(/\s{0,2}\[.*?\]: .*?$/g, "") .replace(/(#{1,6})\s+(.+)\1?/g, '<b>$2</b>')
.replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? "$1" : "") .replace(/\s{0,2}\[.*?\]: .*?$/g, '')
.replace(/\[(.*?)\][\[\(].*?[\]\)]/g, "$1") .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? '$1' : '')
.replace(/^\s{0,3}>\s?/g, "") .replace(/\[(.*?)\][\[\(].*?[\]\)]/g, '<a>$1</a>')
.replace(/(^|\n)\s{0,3}>\s?/g, "\n\n") .replace(/!?\[\[\S[^\[\]\|]*(?:\|([^\[\]]*))?\S\]\]/g, '<a>$1</a>')
.replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "") .replace(/^\s{0,3}>\s?/g, '')
.replace( .replace(/(^|\n)\s{0,3}>\s?/g, '\n\n')
/^(\n)?\s{0,}#{1,6}\s+| {0,}(\n)?\s{0,}#{0,} {0,}(\n)?\s{0,}$/gm, .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, '')
"$1$2$3" .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, '$2')
) .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(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, "$2") .replace(/`(.+?)`/g, '$1')
.replace(/(`{3,})(.*?)\1/gm, "$2") .replace(/\n{2,}/g, '\n\n')
.replace(/`(.+?)`/g, "$1") } catch (e) {
.replace(/\n{2,}/g, "\n\n"); console.error(e)
} catch (e) { return markdown
console.error(e); }
return markdown; return output
} }
return output; // -----
};
// ----- const highlight = (content, term) => {
const highlightWindow = 20
(async function() {
const encoder = str => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])+/) // try to find direct match first
const contentIndex = new FlexSearch.Document({ const directMatchIdx = content.indexOf(term)
cache: true, if (directMatchIdx !== -1) {
charset: "latin:extra", const h = highlightWindow / 2
optimize: true, const before = content.substring(0, directMatchIdx).split(" ").slice(-h)
index: [{ const after = content.substring(directMatchIdx + term.length, content.length - 1).split(" ").slice(0, h)
field: "content", return (before.length == h ? `...${before.join(" ")}` : before.join(" ")) + `<span class="search-highlight">${term}</span>` + after.join(" ")
tokenize: "reverse", }
encode: encoder,
}, { const tokenizedTerm = term.split(/\s+/).filter((t) => t !== '')
field: "title", const splitText = content.split(/\s+/).filter((t) => t !== '')
tokenize: "forward", const includesCheck = (token) =>
encode: encoder, tokenizedTerm.some((term) =>
}] token.toLowerCase().startsWith(term.toLowerCase())
}) )
const { content } = await fetchData const occurrencesIndices = splitText.map(includesCheck)
for (const [key, value] of Object.entries(content)) {
contentIndex.add({ // calculate best index
id: key, let bestSum = 0
title: value.title, let bestIndex = 0
content: removeMarkdown(value.content), for (
}) let i = 0;
} i < Math.max(occurrencesIndices.length - highlightWindow, 0);
i++
const highlight = (content, term) => { ) {
const highlightWindow = 20 const window = occurrencesIndices.slice(i, i + highlightWindow)
const tokenizedTerm = term.split(/\s+/).filter(t => t !== "") const windowSum = window.reduce((total, cur) => total + cur, 0)
const splitText = content.split(/\s+/).filter(t => t !== "") if (windowSum >= bestSum) {
const includesCheck = (token) => tokenizedTerm.some(term => token.toLowerCase().startsWith(term.toLowerCase())) bestSum = windowSum
bestIndex = i
const occurrencesIndices = splitText }
.map(includesCheck) }
// calculate best index const startIndex = Math.max(bestIndex - highlightWindow, 0)
let bestSum = 0 const endIndex = Math.min(
let bestIndex = 0 startIndex + 2 * highlightWindow,
for (let i = 0; i < Math.max(occurrencesIndices.length - highlightWindow, 0); i++) { splitText.length
const window = occurrencesIndices.slice(i, i + highlightWindow) )
const windowSum = window.reduce((total, cur) => total + cur, 0) const mappedText = splitText
if (windowSum >= bestSum) { .slice(startIndex, endIndex)
bestSum = windowSum .map((token) => {
bestIndex = i if (includesCheck(token)) {
} return `<span class="search-highlight">${token}</span>`
} }
return token
const startIndex = Math.max(bestIndex - highlightWindow, 0) })
const endIndex = Math.min(startIndex + 2 * highlightWindow, splitText.length) .join(' ')
const mappedText = splitText .replaceAll('</span> <span class="search-highlight">', ' ')
.slice(startIndex, endIndex) return `${startIndex === 0 ? '' : '...'}${mappedText}${endIndex === splitText.length ? '' : '...'
.map(token => { }`
if (includesCheck(token)) { };
return `<span class="search-highlight">${token}</span>`
} (async function() {
return token const encoder = (str) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])+/)
}) const contentIndex = new FlexSearch.Document({
.join(" ") cache: true,
.replaceAll('</span> <span class="search-highlight">', " ") charset: 'latin:extra',
return `${startIndex === 0 ? "" : "..."}${mappedText}${endIndex === splitText.length ? "" : "..."}` optimize: true,
} index: [
{
const resultToHTML = ({ url, title, content, term }) => { field: 'content',
const text = removeMarkdown(content) tokenize: 'reverse',
const resultTitle = highlight(title, term) encode: encoder,
const resultText = highlight(text, term) },
return `<button class="result-card" id="${url}"> {
<h3>${resultTitle}</h3> field: 'title',
<p>${resultText}</p> tokenize: 'forward',
</button>` encode: encoder,
} },
],
const redir = (id, term) => { })
window.location.href = BASE_URL + `${id}#:~:text=${encodeURIComponent(term)}`
} const { content } = await fetchData
for (const [key, value] of Object.entries(content)) {
const formatForDisplay = id => ({ contentIndex.add({
id, id: key,
url: id, title: value.title,
title: content[id].title, content: removeMarkdown(value.content),
content: content[id].content })
}) }
const source = document.getElementById('search-bar') const resultToHTML = ({ url, title, content, term }) => {
const results = document.getElementById("results-container") const text = removeMarkdown(content)
let term const resultTitle = highlight(title, term)
source.addEventListener("keyup", (e) => { const resultText = highlight(text, term)
if (e.key === "Enter") { return `<button class="result-card" id="${url}">
const anchor = document.getElementsByClassName("result-card")[0] <h3>${resultTitle}</h3>
redir(anchor.id, term) <p>${resultText}</p>
} </button>`
}) }
source.addEventListener('input', (e) => {
term = e.target.value const redir = (id, term) => {
const searchResults = contentIndex.search(term, [ // SPA navigation
{ window.navigate(
field: "content", new URL(
limit: 10, `${BASE_URL.replace(/\/$/g, "")}${id}#:~:text=${encodeURIComponent(term)}/`
}, ),
{ '.singlePage'
field: "title", )
limit: 5, closeSearch()
} }
])
const getByField = field => { const formatForDisplay = (id) => ({
const results = searchResults.filter(x => x.field === field) id,
if (results.length === 0) { url: id,
return [] title: content[id].title,
} else { content: content[id].content,
return [...results[0].result] })
}
} const source = document.getElementById('search-bar')
const allIds = new Set([...getByField('title'), ...getByField('content')]) const results = document.getElementById('results-container')
const finalResults = [...allIds].map(formatForDisplay) let term
source.addEventListener('keyup', (e) => {
// display if (e.key === 'Enter') {
if (finalResults.length === 0) { const anchor = document.getElementsByClassName('result-card')[0]
results.innerHTML = `<button class="result-card"> redir(anchor.id, term)
<h3>No results.</h3> }
<p>Try another search term?</p> })
</button>` source.addEventListener('input', (e) => {
} else { term = e.target.value
results.innerHTML = finalResults const searchResults = contentIndex.search(term, [
.map(result => resultToHTML({ {
...result, field: 'content',
term, limit: 10,
})) },
.join("\n") {
const anchors = document.getElementsByClassName("result-card"); field: 'title',
[...anchors].forEach(anchor => { limit: 5,
anchor.onclick = () => redir(anchor.id, term) },
}) ])
} const getByField = (field) => {
}) const results = searchResults.filter((x) => x.field === field)
if (results.length === 0) {
return []
const searchContainer = document.getElementById("search-container") } else {
return [...results[0].result]
function openSearch() { }
if (searchContainer.style.display === "none" || searchContainer.style.display === "") { }
source.value = "" const allIds = new Set([...getByField('title'), ...getByField('content')])
results.innerHTML = "" const finalResults = [...allIds].map(formatForDisplay)
searchContainer.style.display = "block"
source.focus() // display
} else { if (finalResults.length === 0) {
searchContainer.style.display = "none" results.innerHTML = `<button class="result-card">
} <h3>No results.</h3>
} <p>Try another search term?</p>
</button>`
function closeSearch() { } else {
searchContainer.style.display = "none" results.innerHTML = finalResults
} .map((result) =>
resultToHTML({
document.addEventListener('keydown', (event) => { ...result,
if (event.key === "k" && (event.ctrlKey || event.metaKey)) { term,
event.preventDefault() })
openSearch() )
} .join('\n')
if (event.key === "Escape") { const anchors = [...document.getElementsByClassName('result-card')]
event.preventDefault() anchors.forEach((anchor) => {
closeSearch() anchor.onclick = () => redir(anchor.id, term)
} })
}) }
})
const searchButton = document.getElementById("search-icon")
searchButton.addEventListener('click', (evt) => { const searchContainer = document.getElementById('search-container')
openSearch()
}) function openSearch() {
searchButton.addEventListener('keydown', (evt) => { if (
openSearch() searchContainer.style.display === 'none' ||
}) searchContainer.style.display === ''
searchContainer.addEventListener('click', (evt) => { ) {
closeSearch() source.value = ''
}) results.innerHTML = ''
document.getElementById("search-space").addEventListener('click', (evt) => { searchContainer.style.display = 'block'
evt.stopPropagation() 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()
})
})()

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +1,44 @@
.darkmode { .darkmode {
float: right; float: right;
padding: 1em; padding: 1em;
min-width: 30px; min-width: 30px;
position: relative; position: relative;
@media all and (max-width: 450px) { @media all and (max-width: 450px) {
padding: 1em; padding: 1em;
} }
& > .toggle { & > .toggle {
display: none; display: none;
box-sizing: border-box; box-sizing: border-box;
} }
& svg { & svg {
opacity: 0; opacity: 0;
position: absolute; position: absolute;
width: 20px; width: 20px;
height: 20px; height: 20px;
top: calc(50% - 10px); top: calc(50% - 10px);
margin: 0 7px; margin: 0 7px;
fill: var(--gray); fill: var(--gray);
transition: opacity 0.1s ease; transition: opacity 0.1s ease;
} }
} }
.toggle:checked ~ label { .toggle:checked ~ label {
& > #dayIcon { & > #dayIcon {
opacity: 0; opacity: 0;
} }
& > #nightIcon { & > #nightIcon {
opacity: 1; opacity: 1;
} }
} }
.toggle:not(:checked) ~ label { .toggle:not(:checked) ~ label {
& > #dayIcon { & > #dayIcon {
opacity: 1; opacity: 1;
} }
& > #nightIcon { & > #nightIcon {
opacity: 0; opacity: 0;
} }
} }

View File

@ -1,6 +1,6 @@
enableLegend: false enableLegend: false
enableDrag: true enableDrag: true
enableZoom: true enableZoom: true
depth: -1 # set to -1 to show full graph depth: -1 # set to -1 to show full graph
paths: paths:
- /moc: "#4388cc" - /moc: "#4388cc"

View File

@ -1,16 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
<div class="singlePage"> <div class="singlePage">
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
<div class="centered"> <div class="centered">
<h1>404.</h1> <h1>404.</h1>
<h3>Hey! You look a little lost. This page doesn't exist (or may be private).</h3> <h3>Hey! You look a little lost. This page doesn't exist (or may be private).</h3>
<a href="{{ .Site.BaseURL }}">↳ Let's get you home.</a> <a href="{{ .Site.BaseURL }}">↳ Let's get you home.</a>
</div> </div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -1,8 +1,8 @@
{{$src := .Destination | safeURL }} {{$src := .Destination | safeURL }}
{{$external := strings.HasPrefix $src "http" }} {{$external := strings.HasPrefix $src "http" }}
{{- if $external -}} {{- if $external -}}
<img src="{{ $src }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}" {{ end }} /> <img src="{{ $src }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}" {{ end }} />
{{- else -}} {{- else -}}
{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}} {{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}}
<img src="{{ $fixedUrl }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}" {{ end }} /> <img src="{{ $fixedUrl }}" alt="{{ .Text }}" {{ with .Title }} title="{{ . }}" {{ end }} />
{{- end -}} {{- end -}}

View File

@ -1,16 +1,16 @@
{{$trimmed := strings.TrimSuffix ".md" (.Destination | safeURL)}} {{$trimmed := strings.TrimSuffix ".md" (.Destination | safeURL)}}
{{$dashedurl := replace $trimmed "%20" "-" }} {{$dashedurl := replace $trimmed "%20" "-" }}
{{$external := strings.HasPrefix $dashedurl "http" }} {{$external := strings.HasPrefix $dashedurl "http" }}
{{- if $external -}} {{- if $external -}}
<a href="{{ $dashedurl }}" rel="noopener">{{ .Text | safeHTML }}</a> <a href="{{ $dashedurl }}" rel="noopener">{{ .Text | safeHTML }}</a>
{{- else -}} {{- else -}}
{{$spacedurl := replace $trimmed "%20" " " }} {{$spacedurl := replace $trimmed "%20" " " }}
{{$fixedUrl := (cond (hasPrefix $spacedurl "/") $spacedurl (print "/" $spacedurl)) | urlize}} {{$fixedUrl := (cond (hasPrefix $spacedurl "/") $spacedurl (print "/" $spacedurl)) | urlize}}
{{$nonexistent := eq (.Page.GetPage $spacedurl).RelPermalink ""}} {{$nonexistent := eq (.Page.GetPage $spacedurl).RelPermalink ""}}
{{$rooted := default $spacedurl (strings.TrimRight "/" (.Page.GetPage $spacedurl).RelPermalink) }} {{$rooted := default $spacedurl ((.Page.GetPage $spacedurl).RelPermalink) }}
<a <a
{{if not $nonexistent}}href="{{$rooted}}"{{end}} {{if not $nonexistent}}href="{{$rooted}}"{{end}}
rel="noopener" class="internal-link{{if $nonexistent}} broken{{end}}" rel="noopener" class="internal-link{{if $nonexistent}} broken{{end}}"
data-src="{{$rooted}}">{{- .Text | safeHTML -}} data-src="{{$rooted}}">{{- .Text | safeHTML -}}
</a> </a>
{{- end -}} {{- end -}}

View File

@ -1,10 +1,10 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{ block "head" . }} {{ block "head" . }}
{{ end }} {{ end }}
<body> <body>
{{ block "main" . }} {{ block "main" . }}
{{ end }} {{ end }}
</body> </body>
</html> </html>

View File

@ -1,25 +1,24 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage"> <div class="singlePage">
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1> <h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
<h1>All {{.Title}}</h1> <h1>All {{.Title}}</h1>
{{partial "page-list.html" .Paginator.Pages.ByLastmod.Reverse }} {{partial "page-list.html" .Paginator.Pages.ByLastmod.Reverse }}
{{ template "_internal/pagination.html" .}} {{ template "_internal/pagination.html" .}}
</article> </article>
{{partial "contact.html" .}} {{partial "contact.html" .}}
</div> </div>
{{partial "popover.html" .}} </body>
</body>
</html>
</html>

View File

@ -1,33 +1,32 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ .Lang }}"> <html lang="{{ .Lang }}">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage"> <div class="singlePage">
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1> <h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
{{if .Title}}<h1>{{ .Title }}</h1>{{end}} {{if .Title}}<h1>{{ .Title }}</h1>{{end}}
<p class="meta"> <p class="meta">
Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}}
</p> </p>
<ul class="tags"> <ul class="tags">
{{ range (.GetTerms "tags") }} {{ range (.GetTerms "tags") }}
<li><a href="{{ .Permalink }}">{{ .LinkTitle | humanize }}</a></li> <li><a href="{{ .Permalink }}">{{ .LinkTitle | humanize }}</a></li>
{{ end }} {{ end }}
</ul> </ul>
{{partial "toc.html" .}} {{partial "toc.html" .}}
{{partial "textprocessing.html" . }} {{partial "textprocessing.html" . }}
</article> </article>
{{partial "footer.html" .}} {{partial "footer.html" .}}
{{partial "popover.html" .}} </div>
</div> </body>
</body>
</html>
</html>

View File

@ -1,34 +1,33 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage"> <div class="singlePage">
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1> <h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
<h1>All {{.Title}}</h1> <h1>All {{.Title}}</h1>
<div class="tags"> <div class="tags">
{{ range .Site.Taxonomies.tags.ByCount }} {{ range .Site.Taxonomies.tags.ByCount }}
<div class="meta"> <div class="meta">
<h1><a href="{{ .Page.Permalink }}">{{ .Page.Title }}</a></h1> <h1><a href="{{ .Page.Permalink }}">{{ .Page.Title }}</a></h1>
<p><b>{{ .Count }}</b> notes with this tag {{if gt .Count 2}}(showing first 2 results){{end}}</p> <p><b>{{ .Count }}</b> notes with this tag {{if gt .Count 2}}(showing first 2 results){{end}}</p>
</div> </div>
{{ with ($.Site.GetPage (printf "/tags/%s" .Page.Title)) }} {{ with ($.Site.GetPage (printf "/tags/%s" .Page.Title)) }}
{{partial "page-list.html" (first 2 .Pages.ByLastmod.Reverse)}} {{partial "page-list.html" (first 2 .Pages.ByLastmod.Reverse)}}
{{ end }} {{ end }}
{{ end }} {{ end }}
</div> </div>
</article> </article>
{{partial "contact.html" .}} {{partial "contact.html" .}}
</div> </div>
{{partial "popover.html" .}} </body>
</body>
</html>
</html>

View File

@ -1,25 +1,24 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage"> <div class="singlePage">
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1> <h1 id="page-title"><a href="{{ .Site.BaseURL }}">{{ .Site.Data.config.page_title }}</a></h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
<h1>Tag: {{.Title | humanize}}</h1> <h1>Tag: {{.Title | humanize}}</h1>
{{partial "page-list.html" .Paginator.Pages}} {{partial "page-list.html" .Paginator.Pages}}
{{ template "_internal/pagination.html" . }} {{ template "_internal/pagination.html" . }}
</article> </article>
{{partial "contact.html" .}} {{partial "contact.html" .}}
</div> </div>
{{partial "popover.html" .}} </body>
</body>
</html>
</html>

View File

@ -1,24 +1,22 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{ .Lang }}"> <html lang="{{ .Lang }}">
{{ partial "head.html" . }} {{ partial "head.html" . }}
<body> <body>
{{partial "search.html" .}} {{partial "search.html" .}}
<div class="singlePage"> <div class="singlePage">
<!-- Begin actual content --> <!-- Begin actual content -->
<header> <header>
<h1>{{if .Title}}{{ .Title }}{{else}}Untitled{{end}}</h1> <h1>{{if .Title}}{{ .Title }}{{else}}Untitled{{end}}</h1>
<svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg> <svg tabindex="0" id="search-icon" aria-labelledby="title desc" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.9 19.7"><title id="title">Search Icon</title><desc id="desc">Icon to open search</desc><g class="search-path" fill="none"><path stroke-linecap="square" d="M18.5 18.3l-5.4-5.4"/><circle cx="8" cy="8" r="7"/></g></svg>
<div class="spacer"></div> <div class="spacer"></div>
{{partial "darkmode.html" .}} {{partial "darkmode.html" .}}
</header> </header>
<article> <article>
{{partial "toc.html" .}} {{partial "toc.html" .}}
{{partial "textprocessing.html" . }} {{partial "textprocessing.html" . }}
</article> </article>
{{partial "footer.html" .}} {{partial "footer.html" .}}
{{partial "popover.html" .}} </div>
</div> </body>
</body> </html>
</html>

View File

@ -1,24 +1,30 @@
<h3>Backlinks</h3> <h3>Backlinks</h3>
<ul class="backlinks"> <ul class="backlinks">
{{$url := urls.Parse .Site.BaseURL }} {{$url := urls.Parse .Site.BaseURL }}
{{$host := strings.TrimRight "/" $url.Path }} {{$host := strings.TrimRight "/" $url.Path }}
{{$curPage := strings.TrimPrefix $host (strings.TrimRight "/" .Page.RelPermalink)}} {{$curPage := strings.TrimPrefix $host (strings.TrimRight "/" .Page.RelPermalink)}}
{{$linkIndex := getJSON "/assets/indices/linkIndex.json"}} {{$linkIndex := getJSON "/assets/indices/linkIndex.json"}}
{{$inbound := index $linkIndex.index.backlinks $curPage}} {{$inbound := index $linkIndex.index.backlinks $curPage}}
{{$contentTable := getJSON "/assets/indices/contentIndex.json"}} {{$contentTable := getJSON "/assets/indices/contentIndex.json"}}
{{if $inbound}} {{if $inbound}}
{{$cleanedInbound := apply (apply $inbound "index" "." "source") "replace" "." " " "-"}} {{$backlinks := dict "SENTINEL" "SENTINEL"}}
{{- range $cleanedInbound | uniq -}} {{range $k, $v := $inbound}}
{{$l := printf "%s%s" $host .}} {{$cleanedInbound := replace $v.source " " "-"}}
{{with (index $contentTable .)}} {{$ctx := $v.text}}
<li> {{$backlinks = merge $backlinks (dict $cleanedInbound $ctx)}}
<a href="{{$l}}">{{index (index . "title")}}</a> {{end}}
</li> {{- range $lnk, $ctx := $backlinks -}}
{{end}} {{$l := printf "%s%s/" $host $lnk}}
{{- end -}} {{$l = cond (eq $l "//") "/" $l}}
{{else}} {{with (index $contentTable $lnk)}}
<li> <li>
No backlinks found <a href="{{$l}}" data-ctx="{{$ctx}}" data-src="{{$lnk}}" class="internal-link">{{index (index . "title")}}</a>
</li> </li>
{{end}} {{end}}
</ul> {{- end -}}
{{else}}
<li>
No backlinks found
</li>
{{end}}
</ul>

View File

@ -1,14 +1,14 @@
<!-- Contact Info --> <!-- Contact Info -->
<div id="contact_buttons"> <div id="contact_buttons">
<footer> <footer>
<p>Made by {{ $.Site.Data.config.name }} using <a href="https://github.com/jackyzha0/quartz">Quartz</a>, © {{ dateFormat "2006" now }}</p> <p>Made by {{ $.Site.Data.config.name }} using <a href="https://github.com/jackyzha0/quartz">Quartz</a>, © {{ dateFormat "2006" now }}</p>
<ul> <ul>
{{ if not .IsHome }} {{ if not .IsHome }}
<li><a href="/">Home</a></li> <li><a href="/">Home</a></li>
{{end}} {{end}}
{{- range $.Site.Data.config.links -}} {{- range $.Site.Data.config.links -}}
<li><a href="{{.link}}">{{.link_name}}</a></li> <li><a href="{{.link}}">{{.link_name}}</a></li>
{{- end -}} {{- end -}}
</ul> </ul>
</footer> </footer>
</div> </div>

View File

@ -1,15 +1,15 @@
<div class='darkmode'> <div class='darkmode'>
<input class='toggle' id='darkmode-toggle' type='checkbox' tabindex="-1"> <input class='toggle' id='darkmode-toggle' type='checkbox' tabindex="-1">
<label id="toggle-label-light" for='darkmode-toggle' tabindex="-1"> <label id="toggle-label-light" for='darkmode-toggle' tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="dayIcon" x="0px" y="0px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35;" xml:space="preserve"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="dayIcon" x="0px" y="0px" viewBox="0 0 35 35" style="enable-background:new 0 0 35 35;" xml:space="preserve">
<title>Light Mode</title> <title>Light Mode</title>
<path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z" /> <path d="M6,17.5C6,16.672,5.328,16,4.5,16h-3C0.672,16,0,16.672,0,17.5 S0.672,19,1.5,19h3C5.328,19,6,18.328,6,17.5z M7.5,26c-0.414,0-0.789,0.168-1.061,0.439l-2,2C4.168,28.711,4,29.086,4,29.5 C4,30.328,4.671,31,5.5,31c0.414,0,0.789-0.168,1.06-0.44l2-2C8.832,28.289,9,27.914,9,27.5C9,26.672,8.329,26,7.5,26z M17.5,6 C18.329,6,19,5.328,19,4.5v-3C19,0.672,18.329,0,17.5,0S16,0.672,16,1.5v3C16,5.328,16.671,6,17.5,6z M27.5,9 c0.414,0,0.789-0.168,1.06-0.439l2-2C30.832,6.289,31,5.914,31,5.5C31,4.672,30.329,4,29.5,4c-0.414,0-0.789,0.168-1.061,0.44 l-2,2C26.168,6.711,26,7.086,26,7.5C26,8.328,26.671,9,27.5,9z M6.439,8.561C6.711,8.832,7.086,9,7.5,9C8.328,9,9,8.328,9,7.5 c0-0.414-0.168-0.789-0.439-1.061l-2-2C6.289,4.168,5.914,4,5.5,4C4.672,4,4,4.672,4,5.5c0,0.414,0.168,0.789,0.439,1.06 L6.439,8.561z M33.5,16h-3c-0.828,0-1.5,0.672-1.5,1.5s0.672,1.5,1.5,1.5h3c0.828,0,1.5-0.672,1.5-1.5S34.328,16,33.5,16z M28.561,26.439C28.289,26.168,27.914,26,27.5,26c-0.828,0-1.5,0.672-1.5,1.5c0,0.414,0.168,0.789,0.439,1.06l2,2 C28.711,30.832,29.086,31,29.5,31c0.828,0,1.5-0.672,1.5-1.5c0-0.414-0.168-0.789-0.439-1.061L28.561,26.439z M17.5,29 c-0.829,0-1.5,0.672-1.5,1.5v3c0,0.828,0.671,1.5,1.5,1.5s1.5-0.672,1.5-1.5v-3C19,29.672,18.329,29,17.5,29z M17.5,7 C11.71,7,7,11.71,7,17.5S11.71,28,17.5,28S28,23.29,28,17.5S23.29,7,17.5,7z M17.5,25c-4.136,0-7.5-3.364-7.5-7.5 c0-4.136,3.364-7.5,7.5-7.5c4.136,0,7.5,3.364,7.5,7.5C25,21.636,21.636,25,17.5,25z" />
</svg> </svg>
</label> </label>
<label id="toggle-label-dark" for='darkmode-toggle' tabindex="-1"> <label id="toggle-label-dark" for='darkmode-toggle' tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="nightIcon" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background='new 0 0 100 100'" xml:space="preserve"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="nightIcon" x="0px" y="0px" viewBox="0 0 100 100" style="enable-background='new 0 0 100 100'" xml:space="preserve">
<title>Dark Mode</title> <title>Dark Mode</title>
<path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z" /> <path d="M96.76,66.458c-0.853-0.852-2.15-1.064-3.23-0.534c-6.063,2.991-12.858,4.571-19.655,4.571 C62.022,70.495,50.88,65.88,42.5,57.5C29.043,44.043,25.658,23.536,34.076,6.47c0.532-1.08,0.318-2.379-0.534-3.23 c-0.851-0.852-2.15-1.064-3.23-0.534c-4.918,2.427-9.375,5.619-13.246,9.491c-9.447,9.447-14.65,22.008-14.65,35.369 c0,13.36,5.203,25.921,14.65,35.368s22.008,14.65,35.368,14.65c13.361,0,25.921-5.203,35.369-14.65 c3.872-3.871,7.064-8.328,9.491-13.246C97.826,68.608,97.611,67.309,96.76,66.458z" />
</svg> </svg>
</label> </label>
</div> </div>

View File

@ -1,11 +1,11 @@
<hr/> <hr/>
<div class="page-end"> <div class="page-end">
<div class="backlinks-container"> <div class="backlinks-container">
{{partial "backlinks.html" .}} {{partial "backlinks.html" .}}
</div> </div>
<div> <div>
{{partial "graph.html" .}} {{partial "graph.html" .}}
</div> </div>
</div> </div>
{{partial "contact.html" .}} {{partial "contact.html" .}}

View File

@ -1,25 +1,18 @@
<script src="https://cdn.jsdelivr.net/npm/d3@6.7.0/dist/d3.min.js" integrity="sha256-+7jaYCp29O1JusNWHaYtgUn6EhuP0VaFuswhNV06MyI=" crossorigin="anonymous"></script> <script
<h3>Interactive Graph</h3> src="https://cdn.jsdelivr.net/npm/d3@6.7.0/dist/d3.min.js"
<div id="graph-container"></div> integrity="sha256-+7jaYCp29O1JusNWHaYtgUn6EhuP0VaFuswhNV06MyI="
<style> crossorigin="anonymous"
:root { ></script>
--g-node: var(--secondary); <h3>Interactive Graph</h3>
--g-node-active: var(--primary); <div id="graph-container"></div>
--g-node-inactive: var(--visited); <style>
--g-link: var(--outlinegray); :root {
--g-link-active: #5a7282; --g-node: var(--secondary);
} --g-node-active: var(--primary);
</style> --g-node-inactive: var(--visited);
{{ $js := resources.Get "js/graph.js" | resources.Fingerprint "md5" }} --g-link: var(--outlinegray);
<script src="{{ $js.Permalink }}"></script> --g-link-active: #5a7282;
<script> }
drawGraph( </style>
{{strings.TrimRight "/" .Page.Permalink}}, {{ $js := resources.Get "js/graph.js" | resources.Fingerprint "md5" }}
{{strings.TrimRight "/" .Site.BaseURL}}, <script src="{{ $js.Permalink }}"></script>
{{$.Site.Data.graphConfig.paths}},
{{$.Site.Data.graphConfig.depth}},
{{$.Site.Data.graphConfig.enableDrag}},
{{$.Site.Data.graphConfig.enableLegend}},
{{$.Site.Data.graphConfig.enableZoom}}
)
</script>

View File

@ -1,46 +1,110 @@
<head> <head>
<!-- Meta tags --> <!-- Meta tags -->
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="description" content="{{if .IsHome}}{{$.Site.Data.config.description}}{{else}}{{.Summary}}{{end}}"> <meta
<title>{{ if .Title }}{{ .Title }}{{ else }}{{ $.Site.Data.config.page_title }}{{ end }}</title> name="description"
<meta name="viewport" content="width=device-width, initial-scale=1"> content="{{if .IsHome}}{{$.Site.Data.config.description}}{{else}}{{.Summary}}{{end}}"
<link rel="shortcut icon" type="image/png" href="{{$.Site.BaseURL}}/icon.png" /> />
<title>
<!-- CSS Stylesheets and Fonts --> {{ if .Title }}{{ .Title }}{{ else }}{{ $.Site.Data.config.page_title }}{{
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Source+Sans+Pro:wght@400;600;700&family=Fira+Code:wght@400;700&display=swap" rel="stylesheet"> end }}
{{$sass := resources.Match "styles/[!_]*.scss" }} </title>
{{$css := slice }} <meta name="viewport" content="width=device-width, initial-scale=1" />
{{range $sass}} <link
{{$scss := . | resources.ToCSS (dict "outputStyle" "compressed") }} rel="shortcut icon"
{{$css = $css | append $scss}} type="image/png"
{{end}} href="{{$.Site.BaseURL}}/icon.png"
{{$finalCss := $css | resources.Concat "styles.css" | resources.Fingerprint "md5" | resources.Minify }} />
<link href="{{$finalCss.Permalink}}" rel="stylesheet">
<!-- CSS Stylesheets and Fonts -->
{{ $darkMode := resources.Get "js/darkmode.js" | resources.Fingerprint "md5" | resources.Minify }} <link
<script src="{{$darkMode.Permalink}}"></script> href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=Source+Sans+Pro:wght@400;600;700&family=Fira+Code:wght@400;700&display=swap"
{{partial "katex.html" .}} rel="stylesheet"
/>
<!-- Preload page vars --> {{$sass := resources.Match "styles/[!_]*.scss" }}
{{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint "md5" | resources.Minify | }} {{$css := slice }}
{{$contentIndex := resources.Get "indices/contentIndex.json" | resources.Fingerprint "md5" | resources.Minify }} {{range $sass}}
<script> {{$scss := . | resources.ToCSS (dict "outputStyle" "compressed") }}
const BASE_URL = {{.Site.BaseURL}} {{$css = $css | append $scss}}
const fetchData = Promise.all([ {{end}}
fetch("{{ $linkIndex.Permalink }}") {{$finalCss := $css | resources.Concat "styles.css" | resources.Fingerprint "md5" | resources.Minify }}
.then(data => data.json()) <link href="{{$finalCss.Permalink}}" rel="stylesheet" />
.then(data => ({
index: data.index, {{ $darkMode := resources.Get "js/darkmode.js" | resources.Fingerprint "md5" |
links: data.links, resources.Minify }}
})), <script src="{{$darkMode.Permalink}}"></script>
fetch("{{ $contentIndex.Permalink }}") {{partial "katex.html" .}}
.then(data => data.json()),
]) {{ $popover := resources.Get "js/popover.js" | resources.Fingerprint "md5" |
.then(([{index, links}, content]) => ({ resources.Minify }}
index, <script src="{{$popover.Permalink}}"></script>
links,
content, <!-- Preload page vars -->
})) {{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint
</script> "md5" | resources.Minify | }} {{$contentIndex := resources.Get
</head> "indices/contentIndex.json" | resources.Fingerprint "md5" | resources.Minify
{{ template "_internal/google_analytics.html" . }} }}
<script>
const BASE_URL = {{.Site.BaseURL}}
const fetchData = Promise.all([
fetch("{{ $linkIndex.Permalink }}")
.then(data => data.json())
.then(data => ({
index: data.index,
links: data.links,
})),
fetch("{{ $contentIndex.Permalink }}")
.then(data => data.json()),
])
.then(([{index, links}, content]) => ({
index,
links,
content,
}))
</script>
{{if $.Site.Data.config.enableSPA}}
{{ $router := resources.Get "js/router.js" | resources.Fingerprint "md5" |
resources.Minify }}
<script type="module">
import { attachSPARouting } from '{{$router.Permalink}}';
// NOTE: everything within this callback will be executed for every page navigation. This is a good place to put JavaScript that loads or modifies data on the page.
const draw = () => {
const container = document.getElementById("graph-container")
// retry if the graph is not ready
if (!container) return requestAnimationFrame(draw)
// clear the graph in case there is anything within it
container.textContent = ""
drawGraph(
{{strings.TrimRight "/" .Site.BaseURL}},
{{$.Site.Data.graphConfig.paths}},
{{$.Site.Data.graphConfig.depth}},
{{$.Site.Data.graphConfig.enableDrag}},
{{$.Site.Data.graphConfig.enableLegend}},
{{$.Site.Data.graphConfig.enableZoom}}
);
{{if $.Site.Data.config.enableLinkPreview}}
initPopover(
{{strings.TrimRight "/" .Site.BaseURL }},
{{$.Site.Data.config.enableContextualBacklinks}},
{{$.Site.Data.config.enableLatex}}
)
{{end}}
{{if $.Site.Data.config.enableLatex}}
renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
],
throwOnError : false
});
{{end}}
};
attachSPARouting(draw);
</script>
{{else}}
<script>window.navigate = (url) => window.location.href = url</script>
{{end}}
</head>
{{ template "_internal/google_analytics.html" . }}

View File

@ -1,16 +1,5 @@
{{if $.Site.Data.config.enableLatex}} {{if $.Site.Data.config.enableLatex}}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.css" integrity="sha384-R4558gYOUz8mP9YWpZJjofhk+zx0AS11p36HnD2ZKj/6JR5z27gSSULCNHIRReVs" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.css" integrity="sha384-R4558gYOUz8mP9YWpZJjofhk+zx0AS11p36HnD2ZKj/6JR5z27gSSULCNHIRReVs" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.js" integrity="sha384-z1fJDqw8ZApjGO3/unPWUPsIymfsJmyrDVWC8Tv/a1HeOtGmkwNd/7xUS0Xcnvsx" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/katex.min.js" integrity="sha384-z1fJDqw8ZApjGO3/unPWUPsIymfsJmyrDVWC8Tv/a1HeOtGmkwNd/7xUS0Xcnvsx" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"></script> <script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.1/dist/contrib/auto-render.min.js" integrity="sha384-+XBljXPPiv+OzfbB3cVmLHf4hdUFHlWNZN5spNQ7rmHTXpd7WvJum6fIACpNNfIR" crossorigin="anonymous"></script>
<script> {{end}}
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
],
throwOnError : false
});
});
</script>
{{end}}

View File

@ -1,15 +1,15 @@
<ul class="section-ul"> <ul class="section-ul">
{{- range . -}} {{- range . -}}
<li class="section-li"> <li class="section-li">
<div class="section"> <div class="section">
<div class="desc"> <div class="desc">
<h3><a href="{{ .Permalink }}">{{- .Title -}}</a></h3> <h3><a href="{{ .Permalink }}">{{- .Title -}}</a></h3>
<p>{{- .Summary -}}{{if .Truncated}}...{{end}}</p> <p>{{- .Summary -}}{{if .Truncated}}...{{end}}</p>
</div> </div>
<p class="meta"> <p class="meta">
{{ .ReadingTime }} minute read. Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}} {{ .ReadingTime }} minute read. Last updated {{if ne .Date .Lastmod}}{{ .Lastmod.Format "January 2, 2006" }}{{else}}Unknown{{end}}
</p> </p>
</div> </div>
</li> </li>
{{- end -}} {{- end -}}
</ul> </ul>

View File

@ -1,7 +0,0 @@
{{if $.Site.Data.config.enableLinkPreview}}
{{ $js := resources.Get "js/popover.js" | resources.Fingerprint "md5" | resources.Minify }}
<script src="{{ $js.Permalink }}"></script>
<script>
initPopover({{strings.TrimRight "/" .Site.BaseURL }})
</script>
{{end}}

View File

@ -1,10 +1,10 @@
<div id="search-container"> <div id="search-container">
<div id="search-space"> <div id="search-space">
<input autocomplete="off" id="search-bar" name="search" type="text" aria-label="Search" placeholder="Search for something..."> <input autocomplete="off" id="search-bar" name="search" type="text" aria-label="Search" placeholder="Search for something...">
<div id="results-container"> <div id="results-container">
</div> </div>
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.21/dist/flexsearch.bundle.js" integrity="sha256-i3A0NZGkhsKjVMzFxv3ksk0DZh3aXqu0l49Bbh0MdjE=" crossorigin="anonymous" defer></script> <script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.21/dist/flexsearch.bundle.js" integrity="sha256-i3A0NZGkhsKjVMzFxv3ksk0DZh3aXqu0l49Bbh0MdjE=" crossorigin="anonymous" defer></script>
{{ $js := resources.Get "js/search.js" | resources.Fingerprint "md5" | resources.Minify }} {{ $js := resources.Get "js/search.js" | resources.Fingerprint "md5" | resources.Minify }}
<script defer src="{{ $js.Permalink }}"></script> <script defer src="{{ $js.Permalink }}"></script>

View File

@ -1,56 +1,60 @@
{{ $content := .Content }} {{ $content := .Content }}
{{ $raw := .RawContent }} {{ $raw := .RawContent }}
{{ $page := .Page }} {{ $page := .Page }}
{{/* Escape slashes for Latex to fix line breaks */}} {{/* Escape slashes for Latex to fix line breaks */}}
{{$latex := findRE "\\$\\$([^\\$]+)\\$\\$" $content}} {{$latex := findRE "\\$\\$([^\\$]+)\\$\\$" $content}}
{{range $latex}} {{range $latex}}
{{$fixed := replaceRE "\\\\(?: +|\\n)" "\\\\" .}} {{$fixed := replaceRE "\\\\(?: +|\\n)" "\\\\" .}}
{{$content = replace $content . $fixed}} {{$content = replace $content . $fixed}}
{{end}} {{end}}
{{/* Wikilinks */}} {{/* Wikilinks */}}
{{$wikilinks := $content | findRE "!?\\[\\[\\S[^\\[\\]\\|]*(?:\\|[^\\[\\]]*)?\\S\\]\\]" }} {{$wikilinks := $content | findRE "!?\\[\\[\\S[^\\[\\]\\|]*(?:\\|[^\\[\\]]*)?\\S\\]\\]" }}
{{$codefences := $raw | findRE "\\x60[^\\x60\\n]+\\x60"}} {{$codefences := $raw | findRE "\\x60[^\\x60\\n]+\\x60"}}
{{$codeblocks := $raw | findRE "\\x60{3}[^\\x60]+\\x60{3}"}} {{$codeblocks := $raw | findRE "\\x60{3}[^\\x60]+\\x60{3}"}}
{{$code := union $codefences $codeblocks}} {{$code := union $codefences $codeblocks}}
{{range $wikilinks}} {{range $wikilinks}}
{{$cur := .}} {{$cur := .}}
{{$incode := false}} {{$incode := false}}
{{range $code}} {{range $code}}
{{if (in . $cur)}} {{if (in . $cur)}}
{{$incode = true}} {{$incode = true}}
{{end}} {{end}}
{{end}} {{end}}
{{if not $incode}} {{if not $incode}}
{{if (hasPrefix . "!")}} {{if (hasPrefix . "!")}}
{{$inner := . | strings.TrimPrefix "![[" | strings.TrimSuffix "]]" }} {{$inner := . | strings.TrimPrefix "![[" | strings.TrimSuffix "]]" }}
{{$split := split $inner "|"}} {{$split := split $inner "|"}}
{{$path := index $split 0 | relURL}} {{$path := index $split 0 | relURL}}
{{$reference := split $path "#"}} {{$reference := split $path "#"}}
{{$title := index $reference 0}} {{$title := index $reference 0}}
{{$display := default $title (index $split 1)}} {{$display := default $title (index $split 1)}}
{{$img := printf "<img src=\"%s\" title=\"%s\">" $path $display}} {{$img := printf "<img src=\"%s\" title=\"%s\">" $path $display}}
{{$content = replace $content . $img}} {{$content = replace $content . $img}}
{{else}} {{else}}
{{$inner := . | strings.TrimPrefix "[[" | strings.TrimSuffix "]]" }} {{$inner := . | strings.TrimPrefix "[[" | strings.TrimSuffix "]]" }}
{{$split := split $inner "|"}} {{$split := split $inner "|"}}
{{$path := index $split 0}} {{$path := index $split 0}}
{{$reference := split $path "#"}} {{$reference := split $path "#"}}
{{$title := index $reference 0}} {{$title := index $reference 0}}
{{$block := default "" (index $reference 1)}} {{$block := default "" (index $reference 1)}}
{{$block = strings.TrimRight "/" (cond (eq $block "") $block (printf "#%s" $block))}} {{$block = strings.TrimRight "/" (cond (eq $block "") $block (printf "#%s" $block))}}
{{$href := strings.TrimRight "/" ($page.GetPage $title).RelPermalink}} {{$href := strings.TrimRight "/" ($page.GetPage $title).RelPermalink}}
{{$display := default $title (index $split 1)}} {{$display := default $title (index $split 1)}}
{{if not $href}} {{if not $href}}
{{$link := printf "<a class=\"internal-link broken\">%s</a>" $display}} {{$link := printf "<a class=\"internal-link broken\">%s</a>" $display}}
{{$content = replace $content . $link}} {{$content = replace $content . $link}}
{{else}} {{else}}
{{$fullhref := printf "%s%s" $href $block }} {{$fullhref := printf "%s%s" $href $block }}
{{$link := printf "<a href=\"%s\" rel=\"noopener\" class=\"internal-link\" data-src=\"%s\">%s</a>" $fullhref $href $display}} {{$link := printf "<a href=\"%s\" rel=\"noopener\" class=\"internal-link\" data-src=\"%s\">%s</a>" $fullhref $href $display}}
{{$content = replace $content . $link}} {{$content = replace $content . $link}}
{{end}} {{end}}
{{end}} {{end}}
{{end}} {{end}}
{{end}} {{end}}
{{ $content | safeHTML }}
{{/* Add copyable anchors */}}
{{ $content = $content | replaceRE "(<h[1-9] id=\"([^\"]+)\">)(.+)(</h[1-9]>)" `<a href="#${2}">${1}<span class="hanchor" ariaLabel="Anchor"># </span>${3}${4}</a>` }}
{{ $content | safeHTML }}

View File

@ -1,8 +1,8 @@
{{ if (and $.Site.Data.config.enableToc (ne .Params.enableToc false) (gt .WordCount 250)) }} {{ if (and $.Site.Data.config.enableToc (ne .Params.enableToc false) (gt .WordCount 250)) }}
<aside class="mainTOC"> <aside class="mainTOC">
<details {{ if $.Site.Data.config.openToc }}open {{ end }}> <details {{ if $.Site.Data.config.openToc }}open {{ end }}>
<summary>Table of Contents</summary> <summary>Table of Contents</summary>
{{ .TableOfContents }} {{ .TableOfContents }}
</details> </details>
</aside> </aside>
{{end}} {{end}}