diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index a1147d9cb..c236898ab 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -7,12 +7,14 @@ on:
jobs:
deploy:
- runs-on: ubuntu-18.04
+ runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
+ with:
+ fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Build Link Index
- uses: jackyzha0/hugo-obsidian@v2.12
+ uses: jackyzha0/hugo-obsidian@v2.18
with:
index: true
input: content
diff --git a/.gitignore b/.gitignore
index de43a22fd..4e8f3699f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@ content/.obsidian
assets/indices/linkIndex.json
assets/indices/contentIndex.json
pkg
+linkmap
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..5a88927fa
--- /dev/null
+++ b/Makefile
@@ -0,0 +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
+ 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 --minify
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..058b8ba7b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+# Quartz
+
+Host your second brain and [digital garden](https://jzhao.xyz/posts/networked-thought) for free. Quartz features
+
+1. Extremely fast natural-language search
+2. Customizable and hackable design based on Hugo
+3. Automatically generated backlinks, link previews, and local graph
+4. Built-in CJK + Latex Support and Admonition-style callouts
+5. Support for both Markdown Links and Wikilinks
+
+Check out some of the [amazing gardens that community members](https://quartz.jzhao.xyz/notes/showcase/) have published with Quartz!
+
+> “[One] who works with the door open gets all kinds of interruptions, but [they] also occasionally gets clues as to what the world is and what might be important.” — Richard Hamming
+
+🔗 Get Started: https://quartz.jzhao.xyz/
+
+*Quartz Example Screenshot*
+
+[Join the Discord Community](https://discord.gg/cRFFHYye7t)
diff --git a/assets/js/callouts.js b/assets/js/callouts.js
new file mode 100644
index 000000000..080bbb489
--- /dev/null
+++ b/assets/js/callouts.js
@@ -0,0 +1,6 @@
+const addCollapsibleCallouts = () => {
+ const collapsibleCallouts = document.querySelectorAll("blockquote.callout-collapsible");
+ collapsibleCallouts.forEach(el => el.addEventListener('click', event => {
+ event.currentTarget.classList.toggle("callout-collapsed");
+ }));
+}
diff --git a/assets/js/clipboard.js b/assets/js/clipboard.js
new file mode 100644
index 000000000..cd928c1a4
--- /dev/null
+++ b/assets/js/clipboard.js
@@ -0,0 +1,40 @@
+const svgCopy =
+ '';
+const svgCheck =
+ '';
+
+
+const addCopyButtons = () => {
+ let els = document.getElementsByClassName("highlight");
+ // for each highlight
+ for (let i = 0; i < els.length; i++) {
+ if (els[i].getElementsByClassName("clipboard-button").length) continue;
+
+ // find pre > code inside els[i]
+ let codeBlocks = els[i].getElementsByTagName("code");
+
+ // line numbers are inside first code block
+ let lastCodeBlock = codeBlocks[codeBlocks.length - 1];
+ const button = document.createElement("button");
+ button.className = "clipboard-button";
+ button.type = "button";
+ button.innerHTML = svgCopy;
+ // remove every second newline from lastCodeBlock.innerText
+ button.addEventListener("click", () => {
+ navigator.clipboard.writeText(lastCodeBlock.innerText.replace(/\n\n/g, "\n")).then(
+ () => {
+ button.blur();
+ button.innerHTML = svgCheck;
+ setTimeout(() => {
+ button.innerHTML = svgCopy
+ button.style.borderColor = ""
+ }, 2000);
+ },
+ (error) => (button.innerHTML = "Error")
+ );
+ });
+ // find chroma inside els[i]
+ let chroma = els[i].getElementsByClassName("chroma")[0];
+ els[i].insertBefore(button, chroma);
+ }
+}
diff --git a/assets/js/code-title.js b/assets/js/code-title.js
new file mode 100644
index 000000000..698edc9b6
--- /dev/null
+++ b/assets/js/code-title.js
@@ -0,0 +1,13 @@
+
+function addTitleToCodeBlocks() {
+ var els = document.getElementsByClassName("highlight");
+ for (var i = 0; i < els.length; i++) {
+ if (els[i].title.length) {
+ let div = document.createElement("div");
+ if (els[i].getElementsByClassName("code-title").length) continue;
+ div.textContent=els[i].title;
+ div.classList.add("code-title")
+ els[i].insertBefore(div, els[i].firstChild);
+ }
+ }
+};
diff --git a/assets/js/darkmode.js b/assets/js/darkmode.js
new file mode 100644
index 000000000..610892eb3
--- /dev/null
+++ b/assets/js/darkmode.js
@@ -0,0 +1,40 @@
+const userPref = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
+const currentTheme = localStorage.getItem('theme') ?? userPref
+const syntaxTheme = document.querySelector("#theme-link");
+
+
+{{ $darkSyntax := resources.Get "styles/_dark_syntax.scss" | resources.ToCSS (dict "outputStyle" "compressed") | resources.Fingerprint "md5" | resources.Minify }}
+{{ $lightSyntax := resources.Get "styles/_light_syntax.scss" | resources.ToCSS (dict "outputStyle" "compressed") | resources.Fingerprint "md5" | resources.Minify }}
+
+if (currentTheme) {
+ document.documentElement.setAttribute('saved-theme', currentTheme);
+ syntaxTheme.href = currentTheme === 'dark' ? '{{ $darkSyntax.Permalink }}' : '{{ $lightSyntax.Permalink }}';
+}
+
+const switchTheme = (e) => {
+ if (e.target.checked) {
+ document.documentElement.setAttribute('saved-theme', 'dark');
+ localStorage.setItem('theme', 'dark');
+ syntaxTheme.href = '{{ $darkSyntax.Permalink }}';
+ }
+ else {
+ document.documentElement.setAttribute('saved-theme', 'light')
+ localStorage.setItem('theme', 'light')
+ syntaxTheme.href = '{{ $lightSyntax.Permalink }}';
+ }
+}
+
+if (window) {
+ 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/full-text-search.js b/assets/js/full-text-search.js
new file mode 100644
index 000000000..a8f7f23c6
--- /dev/null
+++ b/assets/js/full-text-search.js
@@ -0,0 +1,61 @@
+; (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 formatForDisplay = (id) => ({
+ id,
+ url: id,
+ title: content[id].title,
+ content: content[id].content,
+ })
+
+ registerHandlers((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)
+ displayResults(finalResults, true)
+ })
+})()
diff --git a/assets/js/graph.js b/assets/js/graph.js
new file mode 100644
index 000000000..c89877b9a
--- /dev/null
+++ b/assets/js/graph.js
@@ -0,0 +1,270 @@
+async function drawGraph(baseUrl, isHome, pathColors, graphConfig) {
+
+ let {
+ depth,
+ enableDrag,
+ enableLegend,
+ enableZoom,
+ opacityScale,
+ scale,
+ repelForce,
+ fontSize} = graphConfig;
+
+ 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, isHome ? 500 : 250)
+ const width = container.offsetWidth
+
+ const simulation = d3
+ .forceSimulation(data.nodes)
+ .force("charge", d3.forceManyBody().strength(-100 * repelForce))
+ .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 * 1 / scale, -height / 2 * 1 / scale, width * 1 / scale, height * 1 / scale])
+
+ 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 2 + Math.sqrt(numOut + numIn)
+ }
+
+ // 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.Million.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
+ window.Million.prefetch(new URL(`${baseUrl}${decodeURI(d.id).replace(/\s+/g, "-")}/`))
+ 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)")
+
+ const bigFont = fontSize*1.5
+
+ // show text for self
+ d3.select(this.parentNode)
+ .raise()
+ .select("text")
+ .transition()
+ .duration(200)
+ .attr('opacityOld', d3.select(this.parentNode).select('text').style("opacity"))
+ .style('opacity', 1)
+ .style('font-size', bigFont+'em')
+ .attr('dy', d => nodeRadius(d) + 20 + 'px') // radius is in px
+ })
+ .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', d3.select(this.parentNode).select('text').attr("opacityOld"))
+ .style('font-size', fontSize+'em')
+ .attr('dy', d => nodeRadius(d) + 8 + 'px') // radius is in px
+ })
+ .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', (opacityScale - 1) / 3.75)
+ .style("pointer-events", "none")
+ .style('font-size', fontSize+'em')
+ .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 * opacityScale;
+ 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
new file mode 100644
index 000000000..29104b919
--- /dev/null
+++ b/assets/js/popover.js
@@ -0,0 +1,74 @@
+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) {
+ let splitLink = li.href.split("#")
+ let cleanedContent = removeMarkdown(linkDest.content)
+ if (splitLink.length > 1) {
+ let headingName = decodeURIComponent(splitLink[1]).replace(/\-/g, " ")
+ let headingIndex = cleanedContent.toLowerCase().indexOf("" + headingName + "")
+ cleanedContent = cleanedContent.substring(headingIndex, cleanedContent.length)
+ }
+ const popoverElement = `
+
${linkDest.title}
+
${cleanedContent.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 },
+ ],
+ throwOnError: false
+ })
+ }
+
+ li.addEventListener("mouseover", () => {
+ // fix tooltip positioning
+ window.FloatingUIDOM.computePosition(li, el, {
+ middleware: [window.FloatingUIDOM.offset(10), window.FloatingUIDOM.inline(), window.FloatingUIDOM.shift()],
+ }).then(({ x, y }) => {
+ Object.assign(el.style, {
+ left: `${x}px`,
+ top: `${y}px`,
+ })
+ })
+
+ 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..efaa10e8a
--- /dev/null
+++ b/assets/js/router.js
@@ -0,0 +1,26 @@
+import {
+ apply,
+ navigate,
+ prefetch,
+ router,
+} from "https://unpkg.com/million@1.11.5/dist/router.mjs"
+
+export const attachSPARouting = (init, rerender) => {
+ // Attach SPA functions to the global Million namespace
+ window.Million = {
+ apply,
+ navigate,
+ prefetch,
+ router,
+ }
+
+ const render = () => requestAnimationFrame(rerender)
+
+ window.addEventListener("DOMContentLoaded", () => {
+ apply((doc) => init(doc))
+ init()
+ router(".singlePage")
+ render()
+ })
+ window.addEventListener("million:navigate", render)
+}
diff --git a/assets/js/semantic-search.js b/assets/js/semantic-search.js
new file mode 100644
index 000000000..4382817ce
--- /dev/null
+++ b/assets/js/semantic-search.js
@@ -0,0 +1,38 @@
+const apiKey = "{{$.Site.Data.config.operandApiKey}}"
+
+async function searchContents(query) {
+ const response = await fetch('https://prod.operand.ai/v3/search/objects', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Authorization: apiKey,
+ },
+ body: JSON.stringify({
+ query,
+ max: 10
+ }),
+ });
+ return (await response.json());
+}
+
+function debounce(func, timeout = 200) {
+ let timer;
+ return (...args) => {
+ clearTimeout(timer)
+ timer = setTimeout(() => { func.apply(this, args); }, timeout)
+ };
+}
+
+registerHandlers(debounce((e) => {
+ term = e.target.value
+ if (term !== "") {
+ searchContents(term)
+ .then((res) => res.results.map(entry => ({
+ url: entry.object.properties.url,
+ content: entry.snippet,
+ title: entry.object.metadata.title
+ })
+ ))
+ .then(results => displayResults(results))
+ }
+}))
diff --git a/assets/js/util.js b/assets/js/util.js
new file mode 100644
index 000000000..06db3c1b7
--- /dev/null
+++ b/assets/js/util.js
@@ -0,0 +1,209 @@
+// 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")
+ .replace(/\[![a-zA-Z]+\][-\+]? /g, "")
+ } 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
+ const before = content.substring(0, directMatchIdx).split(" ").slice(-h)
+ const after = content
+ .substring(directMatchIdx + term.length, content.length - 2)
+ .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 ? "" : "..."
+ }`
+}
+
+// Common utilities for search
+const resultToHTML = ({ url, title, content }) => {
+ return ``
+}
+
+const redir = (id, term) => {
+ // SPA navigation
+ window.Million.navigate(
+ new URL(`${BASE_URL.replace(/\/$/g, "")}${id}#:~:text=${encodeURIComponent(term)}/`),
+ ".singlePage",
+ )
+ closeSearch()
+}
+
+function openSearch() {
+ const source = document.getElementById("search-bar")
+ const results = document.getElementById("results-container")
+ const searchContainer = document.getElementById("search-container")
+ 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() {
+ const searchContainer = document.getElementById("search-container")
+ searchContainer.style.display = "none"
+}
+
+const registerHandlers = (onInputFn) => {
+ const source = document.getElementById("search-bar")
+ const searchContainer = document.getElementById("search-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", onInputFn)
+ 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", (_) => {
+ openSearch()
+ })
+ searchButton.addEventListener("keydown", (_) => {
+ openSearch()
+ })
+ searchContainer.addEventListener("click", (_) => {
+ closeSearch()
+ })
+ document.getElementById("search-space").addEventListener("click", (evt) => {
+ evt.stopPropagation()
+ })
+}
+
+const displayResults = (finalResults, extractHighlight = false) => {
+ const results = document.getElementById("results-container")
+ if (finalResults.length === 0) {
+ results.innerHTML = ``
+ } else {
+ results.innerHTML = finalResults
+ .map((result) => {
+ if (extractHighlight) {
+ return resultToHTML({
+ url: result.url,
+ title: highlight(result.title, term),
+ content: highlight(removeMarkdown(result.content), term)
+ })
+ } else {
+ return resultToHTML(result)
+ }
+ }
+ )
+ .join("\n")
+ const anchors = [...document.getElementsByClassName("result-card")]
+ anchors.forEach((anchor) => {
+ anchor.onclick = () => redir(anchor.id, term)
+ })
+ }
+}
diff --git a/assets/styles/_callouts.scss b/assets/styles/_callouts.scss
new file mode 100644
index 000000000..04fd2f662
--- /dev/null
+++ b/assets/styles/_callouts.scss
@@ -0,0 +1,170 @@
+:root {
+ --callout-summary: #00b0ff;
+ --callout-summary-accent: #7fd7ff;
+ --callout-bug: #f50057;
+ --callout-bug-accent: #ff7aa9;
+ --callout-danger: #ff1744;
+ --callout-danger-accent: #ff8aa1;
+ --callout-example: #7c4dff;
+ --callout-example-accent: #bda5ff;
+ --callout-fail: #ff5252;
+ --callout-fail-accent: #ffa8a8;
+ --callout-info: #00b8d4;
+ --callout-info-accent: #69ebff;
+ --callout-note: #448aff;
+ --callout-note-accent: #a1c4ff;
+ --callout-question: #64dd17;
+ --callout-question-accent: #b0f286;
+ --callout-quote: #9e9e9e;
+ --callout-quote-accent: #cecece;
+ --callout-done: #00c853;
+ --callout-done-accent: #63ffa4;
+ --callout-important: #00bfa5;
+ --callout-important-accent: #5fffe9;
+ --callout-warning: #ff9100;
+ --callout-warning-accent: #ffc87f;
+}
+
+[saved-theme=dark] {
+ --callout-summary: #00b0ff !important;
+ --callout-summary-accent: #00587f !important;
+ --callout-bug: #f50057 !important;
+ --callout-bug-accent: #7a002b !important;
+ --callout-danger: #ff1744 !important;
+ --callout-danger-accent: #8b001a !important;
+ --callout-example: #7c4dff !important;
+ --callout-example-accent: #2b00a6 !important;
+ --callout-fail: #ff5252 !important;
+ --callout-fail-accent: #a80000 !important;
+ --callout-info: #00b8d4 !important;
+ --callout-info-accent: #005c6a !important;
+ --callout-note: #448aff !important;
+ --callout-note-accent: #003ca1 !important;
+ --callout-question: #64dd17 !important;
+ --callout-question-accent: #006429 !important;
+ --callout-quote: #9e9e9e !important;
+ --callout-quote-accent: #4f4f4f !important;
+ --callout-done: #00c853 !important;
+ --callout-done-accent: #006429 !important;
+ --callout-important: #00bfa5 !important;
+ --callout-important-accent: #005f52 !important;
+ --callout-warning: #ff9100 !important;
+ --callout-warning-accent: #7f4800 !important;
+}
+
+blockquote.callout-collapsible {
+ cursor: pointer;
+
+ &.callout-collapsible::after {
+ content: '-';
+ right: 6px;
+ font-weight: bolder;
+ font-family: Courier New, Courier, monospace;
+ }
+}
+
+blockquote.callout-collapsed {
+ & > p { border-bottom-right-radius: 5px !important; }
+ padding-bottom: 0 !important;
+ &::after {
+ content: '+' !important;
+ }
+ & > *:not(:first-child) {
+ display: none !important;
+ }
+}
+
+blockquote[class*="-callout"] {
+ margin-right: 0;
+ border-radius: 5px;
+ position: relative;
+ padding-left: 0 !important;
+ padding-bottom: 0.25em;
+ color: var(--dark);
+ background-color: var(--lightgray);
+ border-left: 6px solid var(--primary) !important;
+ & > p {
+ border-top-right-radius: 5px;
+ padding: 0.5em 1em;
+ margin: 0;
+ color: var(--gray);
+ &:first-child {
+ font-weight: 600;
+ color: var(--dark);
+ padding: 0.4em 30px;
+ }
+ }
+}
+
+blockquote[class*="-callout"] > p:first-child::after, blockquote.callout-collapsible::after {
+ display: inline-block;
+ height: 18px;
+ width: 18px;
+ position: absolute;
+ top: 0.4em;
+ margin: 0.2em 0.4em;
+}
+
+blockquote[class*="-callout"] > p:first-child {
+ font-weight: bold;
+ padding: 0.4em 35px;
+
+ &::after {
+ left: 0;
+ }
+}
+
+$summary: summary, abstract, tldr;
+$bug: bug;
+$danger: danger, error;
+$example: example;
+$fail: fail, failure, missing;
+$info: info, todo;
+$note: note;
+$question: question, help, faq;
+$quote: quote, cite;
+$done: done, success, check;
+$important: important, tip, hint;
+$warning: warning, caution, attention;
+$types: $summary, $bug, $danger, $example, $fail, $info, $note, $question, $quote, $done, $important, $warning;
+$svgs: ();
+$svgs: map-merge($svgs, ($summary: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='book' class='svg-inline--callout-fa fa-book fa-w-14' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath fill='currentColor' d='M448 360V24c0-13.3-10.7-24-24-24H96C43 0 0 43 0 96v320c0 53 43 96 96 96h328c13.3 0 24-10.7 24-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3 0-74.7 5.4-4.3 8.9-11.1 8.9-18.6zM128 134c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm0 64c0-3.3 2.7-6 6-6h212c3.3 0 6 2.7 6 6v20c0 3.3-2.7 6-6 6H134c-3.3 0-6-2.7-6-6v-20zm253.4 250H96c-17.7 0-32-14.3-32-32 0-17.6 14.4-32 32-32h285.4c-1.9 17.1-1.9 46.9 0 64z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($bug: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='bug' class='svg-inline--callout-fa fa-bug fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M511.988 288.9c-.478 17.43-15.217 31.1-32.653 31.1H424v16c0 21.864-4.882 42.584-13.6 61.145l60.228 60.228c12.496 12.497 12.496 32.758 0 45.255-12.498 12.497-32.759 12.496-45.256 0l-54.736-54.736C345.886 467.965 314.351 480 280 480V236c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v244c-34.351 0-65.886-12.035-90.636-32.108l-54.736 54.736c-12.498 12.497-32.759 12.496-45.256 0-12.496-12.497-12.496-32.758 0-45.255l60.228-60.228C92.882 378.584 88 357.864 88 336v-16H32.666C15.23 320 .491 306.33.013 288.9-.484 270.816 14.028 256 32 256h56v-58.745l-46.628-46.628c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0L141.255 160h229.489l54.627-54.627c12.498-12.497 32.758-12.497 45.256 0 12.496 12.497 12.496 32.758 0 45.255L424 197.255V256h56c17.972 0 32.484 14.816 31.988 32.9zM257 0c-61.856 0-112 50.144-112 112h224C369 50.144 318.856 0 257 0z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($danger: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='bolt' class='svg-inline--callout-fa fa-bolt fa-w-10' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 512'%3E%3Cpath fill='currentColor' d='M296 160H180.6l42.6-129.8C227.2 15 215.7 0 200 0H56C44 0 33.8 8.9 32.2 20.8l-32 240C-1.7 275.2 9.5 288 24 288h118.7L96.6 482.5c-3.6 15.2 8 29.5 23.3 29.5 8.4 0 16.4-4.4 20.8-12l176-304c9.3-15.9-2.2-36-20.7-36z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($example: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='list-ol' class='svg-inline--callout-fa fa-list-ol fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M61.77 401l17.5-20.15a19.92 19.92 0 0 0 5.07-14.19v-3.31C84.34 356 80.5 352 73 352H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8h22.83a157.41 157.41 0 0 0-11 12.31l-5.61 7c-4 5.07-5.25 10.13-2.8 14.88l1.05 1.93c3 5.76 6.29 7.88 12.25 7.88h4.73c10.33 0 15.94 2.44 15.94 9.09 0 4.72-4.2 8.22-14.36 8.22a41.54 41.54 0 0 1-15.47-3.12c-6.49-3.88-11.74-3.5-15.6 3.12l-5.59 9.31c-3.72 6.13-3.19 11.72 2.63 15.94 7.71 4.69 20.38 9.44 37 9.44 34.16 0 48.5-22.75 48.5-44.12-.03-14.38-9.12-29.76-28.73-34.88zM496 224H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zm0-160H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16V80a16 16 0 0 0-16-16zm0 320H176a16 16 0 0 0-16 16v32a16 16 0 0 0 16 16h320a16 16 0 0 0 16-16v-32a16 16 0 0 0-16-16zM16 160h64a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H64V40a8 8 0 0 0-8-8H32a8 8 0 0 0-7.14 4.42l-8 16A8 8 0 0 0 24 64h8v64H16a8 8 0 0 0-8 8v16a8 8 0 0 0 8 8zm-3.91 160H80a8 8 0 0 0 8-8v-16a8 8 0 0 0-8-8H41.32c3.29-10.29 48.34-18.68 48.34-56.44 0-29.06-25-39.56-44.47-39.56-21.36 0-33.8 10-40.46 18.75-4.37 5.59-3 10.84 2.8 15.37l8.58 6.88c5.61 4.56 11 2.47 16.12-2.44a13.44 13.44 0 0 1 9.46-3.84c3.33 0 9.28 1.56 9.28 8.75C51 248.19 0 257.31 0 304.59v4C0 316 5.08 320 12.09 320z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($fail: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='times-circle' class='svg-inline--callout-fa fa-times-circle fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($info: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='info-circle' class='svg-inline--callout-fa fa-info-circle fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($note: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='pencil-alt' class='svg-inline--callout-fa fa-pencil-alt fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($question: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='question-circle' class='svg-inline--callout-fa fa-question-circle fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zM262.655 90c-54.497 0-89.255 22.957-116.549 63.758-3.536 5.286-2.353 12.415 2.715 16.258l34.699 26.31c5.205 3.947 12.621 3.008 16.665-2.122 17.864-22.658 30.113-35.797 57.303-35.797 20.429 0 45.698 13.148 45.698 32.958 0 14.976-12.363 22.667-32.534 33.976C247.128 238.528 216 254.941 216 296v4c0 6.627 5.373 12 12 12h56c6.627 0 12-5.373 12-12v-1.333c0-28.462 83.186-29.647 83.186-106.667 0-58.002-60.165-102-116.531-102zM256 338c-25.365 0-46 20.635-46 46 0 25.364 20.635 46 46 46s46-20.636 46-46c0-25.365-20.635-46-46-46z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($quote: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='quote-right' class='svg-inline--callout-fa fa-quote-right fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M464 32H336c-26.5 0-48 21.5-48 48v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48zm-288 0H48C21.5 32 0 53.5 0 80v128c0 26.5 21.5 48 48 48h80v64c0 35.3-28.7 64-64 64h-8c-13.3 0-24 10.7-24 24v48c0 13.3 10.7 24 24 24h8c88.4 0 160-71.6 160-160V80c0-26.5-21.5-48-48-48z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($done: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='check-circle' class='svg-inline--callout-fa fa-check-circle fa-w-16' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath fill='currentColor' d='M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($important: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='fire' class='svg-inline--callout-fa fa-fire fa-w-12' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 384 512'%3E%3Cpath fill='currentColor' d='M216 23.86c0-23.8-30.65-32.77-44.15-13.04C48 191.85 224 200 224 288c0 35.63-29.11 64.46-64.85 63.99-35.17-.45-63.15-29.77-63.15-64.94v-85.51c0-21.7-26.47-32.23-41.43-16.5C27.8 213.16 0 261.33 0 320c0 105.87 86.13 192 192 192s192-86.13 192-192c0-170.29-168-193-168-296.14z'%3E%3C/path%3E%3C/svg%3E")));
+$svgs: map-merge($svgs, ($warning: url("data:image/svg+xml,%3Csvg aria-hidden='true' focusable='false' data-icon='exclamation-triangle' class='svg-inline--callout-fa fa-exclamation-triangle fa-w-18' role='img' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 576 512'%3E%3Cpath fill='currentColor' d='M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z'%3E%3C/path%3E%3C/svg%3E")));
+
+@function getstr($l) {
+ $v: nth($l, 1);
+ @return $v;
+}
+
+@each $type in $types {
+ @each $s in $type {
+ blockquote.#{$s}-callout {
+ border-left: 6px solid var(--callout-#{getstr($type)}) !important;
+ & > p:first-child {
+ background-color: var(--callout-#{getstr($type)}-accent) !important;
+ &::after {
+ content: '';
+ -webkit-mask: map-get($svgs, $type);
+ mask: map-get($svgs, $type);
+ background-color: var(--callout-#{getstr($type)}) !important;
+ -webkit-mask-size: contain;
+ mask-size: contain;
+ -webkit-mask-repeat: no-repeat;
+ mask-repeat: no-repeat;
+ -webkit-mask-position: center;
+ mask-position: center;
+ }
+ }
+ }
+ }
+}
diff --git a/assets/styles/_dark_syntax.scss b/assets/styles/_dark_syntax.scss
new file mode 100644
index 000000000..9d2019a8a
--- /dev/null
+++ b/assets/styles/_dark_syntax.scss
@@ -0,0 +1,85 @@
+/* Background */ .bg { color: #f8f8f2; background-color: #282a36; }
+/* PreWrapper */ .chroma { color: #f8f8f2; background-color: #282a36; }
+/* Other */ .chroma .x { }
+/* Error */ .chroma .err { }
+/* CodeLine */ .chroma .cl { }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
+/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
+/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Line */ .chroma .line { display: flex; }
+/* Keyword */ .chroma .k { color: #ff79c6 }
+/* KeywordConstant */ .chroma .kc { color: #ff79c6 }
+/* KeywordDeclaration */ .chroma .kd { color: #8be9fd; font-style: italic }
+/* KeywordNamespace */ .chroma .kn { color: #ff79c6 }
+/* KeywordPseudo */ .chroma .kp { color: #ff79c6 }
+/* KeywordReserved */ .chroma .kr { color: #ff79c6 }
+/* KeywordType */ .chroma .kt { color: #8be9fd }
+/* Name */ .chroma .n { }
+/* NameAttribute */ .chroma .na { color: #50fa7b }
+/* NameBuiltin */ .chroma .nb { color: #8be9fd; font-style: italic }
+/* NameBuiltinPseudo */ .chroma .bp { }
+/* NameClass */ .chroma .nc { color: #50fa7b }
+/* NameConstant */ .chroma .no { }
+/* NameDecorator */ .chroma .nd { }
+/* NameEntity */ .chroma .ni { }
+/* NameException */ .chroma .ne { }
+/* NameFunction */ .chroma .nf { color: #50fa7b }
+/* NameFunctionMagic */ .chroma .fm { }
+/* NameLabel */ .chroma .nl { color: #8be9fd; font-style: italic }
+/* NameNamespace */ .chroma .nn { }
+/* NameOther */ .chroma .nx { }
+/* NameProperty */ .chroma .py { }
+/* NameTag */ .chroma .nt { color: #ff79c6 }
+/* NameVariable */ .chroma .nv { color: #8be9fd; font-style: italic }
+/* NameVariableClass */ .chroma .vc { color: #8be9fd; font-style: italic }
+/* NameVariableGlobal */ .chroma .vg { color: #8be9fd; font-style: italic }
+/* NameVariableInstance */ .chroma .vi { color: #8be9fd; font-style: italic }
+/* NameVariableMagic */ .chroma .vm { }
+/* Literal */ .chroma .l { }
+/* LiteralDate */ .chroma .ld { }
+/* LiteralString */ .chroma .s { color: #f1fa8c }
+/* LiteralStringAffix */ .chroma .sa { color: #f1fa8c }
+/* LiteralStringBacktick */ .chroma .sb { color: #f1fa8c }
+/* LiteralStringChar */ .chroma .sc { color: #f1fa8c }
+/* LiteralStringDelimiter */ .chroma .dl { color: #f1fa8c }
+/* LiteralStringDoc */ .chroma .sd { color: #f1fa8c }
+/* LiteralStringDouble */ .chroma .s2 { color: #f1fa8c }
+/* LiteralStringEscape */ .chroma .se { color: #f1fa8c }
+/* LiteralStringHeredoc */ .chroma .sh { color: #f1fa8c }
+/* LiteralStringInterpol */ .chroma .si { color: #f1fa8c }
+/* LiteralStringOther */ .chroma .sx { color: #f1fa8c }
+/* LiteralStringRegex */ .chroma .sr { color: #f1fa8c }
+/* LiteralStringSingle */ .chroma .s1 { color: #f1fa8c }
+/* LiteralStringSymbol */ .chroma .ss { color: #f1fa8c }
+/* LiteralNumber */ .chroma .m { color: #bd93f9 }
+/* LiteralNumberBin */ .chroma .mb { color: #bd93f9 }
+/* LiteralNumberFloat */ .chroma .mf { color: #bd93f9 }
+/* LiteralNumberHex */ .chroma .mh { color: #bd93f9 }
+/* LiteralNumberInteger */ .chroma .mi { color: #bd93f9 }
+/* LiteralNumberIntegerLong */ .chroma .il { color: #bd93f9 }
+/* LiteralNumberOct */ .chroma .mo { color: #bd93f9 }
+/* Operator */ .chroma .o { color: #ff79c6 }
+/* OperatorWord */ .chroma .ow { color: #ff79c6 }
+/* Punctuation */ .chroma .p { }
+/* Comment */ .chroma .c { color: #6272a4 }
+/* CommentHashbang */ .chroma .ch { color: #6272a4 }
+/* CommentMultiline */ .chroma .cm { color: #6272a4 }
+/* CommentSingle */ .chroma .c1 { color: #6272a4 }
+/* CommentSpecial */ .chroma .cs { color: #6272a4 }
+/* CommentPreproc */ .chroma .cp { color: #ff79c6 }
+/* CommentPreprocFile */ .chroma .cpf { color: #ff79c6 }
+/* Generic */ .chroma .g { }
+/* GenericDeleted */ .chroma .gd { color: #ff5555 }
+/* GenericEmph */ .chroma .ge { text-decoration: underline }
+/* GenericError */ .chroma .gr { }
+/* GenericHeading */ .chroma .gh { font-weight: bold }
+/* GenericInserted */ .chroma .gi { color: #50fa7b; font-weight: bold }
+/* GenericOutput */ .chroma .go { color: #44475a }
+/* GenericPrompt */ .chroma .gp { }
+/* GenericStrong */ .chroma .gs { }
+/* GenericSubheading */ .chroma .gu { font-weight: bold }
+/* GenericTraceback */ .chroma .gt { }
+/* GenericUnderline */ .chroma .gl { text-decoration: underline }
+/* TextWhitespace */ .chroma .w { }
diff --git a/assets/styles/_light_syntax.scss b/assets/styles/_light_syntax.scss
new file mode 100644
index 000000000..d0f452e03
--- /dev/null
+++ b/assets/styles/_light_syntax.scss
@@ -0,0 +1,85 @@
+/* Background */ .bg { color: #272822; background-color: #fafafa; }
+/* PreWrapper */ .chroma { color: #272822; background-color: #fafafa; }
+/* Other */ .chroma .x { }
+/* Error */ .chroma .err { }
+/* CodeLine */ .chroma .cl { }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
+/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
+/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Line */ .chroma .line { display: flex; }
+/* Keyword */ .chroma .k { color: #00a8c8 }
+/* KeywordConstant */ .chroma .kc { color: #00a8c8 }
+/* KeywordDeclaration */ .chroma .kd { color: #00a8c8 }
+/* KeywordNamespace */ .chroma .kn { color: #f92672 }
+/* KeywordPseudo */ .chroma .kp { color: #00a8c8 }
+/* KeywordReserved */ .chroma .kr { color: #00a8c8 }
+/* KeywordType */ .chroma .kt { color: #00a8c8 }
+/* Name */ .chroma .n { color: #111111 }
+/* NameAttribute */ .chroma .na { color: #75af00 }
+/* NameBuiltin */ .chroma .nb { color: #111111 }
+/* NameBuiltinPseudo */ .chroma .bp { color: #111111 }
+/* NameClass */ .chroma .nc { color: #75af00 }
+/* NameConstant */ .chroma .no { color: #00a8c8 }
+/* NameDecorator */ .chroma .nd { color: #75af00 }
+/* NameEntity */ .chroma .ni { color: #111111 }
+/* NameException */ .chroma .ne { color: #75af00 }
+/* NameFunction */ .chroma .nf { color: #75af00 }
+/* NameFunctionMagic */ .chroma .fm { color: #111111 }
+/* NameLabel */ .chroma .nl { color: #111111 }
+/* NameNamespace */ .chroma .nn { color: #111111 }
+/* NameOther */ .chroma .nx { color: #75af00 }
+/* NameProperty */ .chroma .py { color: #111111 }
+/* NameTag */ .chroma .nt { color: #f92672 }
+/* NameVariable */ .chroma .nv { color: #111111 }
+/* NameVariableClass */ .chroma .vc { color: #111111 }
+/* NameVariableGlobal */ .chroma .vg { color: #111111 }
+/* NameVariableInstance */ .chroma .vi { color: #111111 }
+/* NameVariableMagic */ .chroma .vm { color: #111111 }
+/* Literal */ .chroma .l { color: #ae81ff }
+/* LiteralDate */ .chroma .ld { color: #d88200 }
+/* LiteralString */ .chroma .s { color: #d88200 }
+/* LiteralStringAffix */ .chroma .sa { color: #d88200 }
+/* LiteralStringBacktick */ .chroma .sb { color: #d88200 }
+/* LiteralStringChar */ .chroma .sc { color: #d88200 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #d88200 }
+/* LiteralStringDoc */ .chroma .sd { color: #d88200 }
+/* LiteralStringDouble */ .chroma .s2 { color: #d88200 }
+/* LiteralStringEscape */ .chroma .se { color: #8045ff }
+/* LiteralStringHeredoc */ .chroma .sh { color: #d88200 }
+/* LiteralStringInterpol */ .chroma .si { color: #d88200 }
+/* LiteralStringOther */ .chroma .sx { color: #d88200 }
+/* LiteralStringRegex */ .chroma .sr { color: #d88200 }
+/* LiteralStringSingle */ .chroma .s1 { color: #d88200 }
+/* LiteralStringSymbol */ .chroma .ss { color: #d88200 }
+/* LiteralNumber */ .chroma .m { color: #ae81ff }
+/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
+/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
+/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
+/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
+/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
+/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
+/* Operator */ .chroma .o { color: #f92672 }
+/* OperatorWord */ .chroma .ow { color: #f92672 }
+/* Punctuation */ .chroma .p { color: #111111 }
+/* Comment */ .chroma .c { color: #75715e }
+/* CommentHashbang */ .chroma .ch { color: #75715e }
+/* CommentMultiline */ .chroma .cm { color: #75715e }
+/* CommentSingle */ .chroma .c1 { color: #75715e }
+/* CommentSpecial */ .chroma .cs { color: #75715e }
+/* CommentPreproc */ .chroma .cp { color: #75715e }
+/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
+/* Generic */ .chroma .g { }
+/* GenericDeleted */ .chroma .gd { }
+/* GenericEmph */ .chroma .ge { font-style: italic }
+/* GenericError */ .chroma .gr { }
+/* GenericHeading */ .chroma .gh { }
+/* GenericInserted */ .chroma .gi { }
+/* GenericOutput */ .chroma .go { }
+/* GenericPrompt */ .chroma .gp { }
+/* GenericStrong */ .chroma .gs { font-weight: bold }
+/* GenericSubheading */ .chroma .gu { }
+/* GenericTraceback */ .chroma .gt { }
+/* GenericUnderline */ .chroma .gl { }
+/* TextWhitespace */ .chroma .w { }
diff --git a/assets/styles/base.scss b/assets/styles/base.scss
new file mode 100644
index 000000000..7637b7ed5
--- /dev/null
+++ b/assets/styles/base.scss
@@ -0,0 +1,644 @@
+// Replace this with your own font imports!
+@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;700&family=Inter:wght@400;600;700&family=Source+Sans+Pro:wght@400;600&display=swap');
+:root {
+ --font-body: "Source Sans Pro";
+ --font-header: "Inter";
+ --font-mono: "Fira Code"
+}
+
+
+img[alt="overment"] { width: 200px; display: block; margin-inline: auto; }
+
+h1, h2, h3, h4, h5, h6, ol, ul, thead {
+ font-family: Inter;
+ // typography
+ html {
+ scroll-behavior: smooth;
+
+ &:lang(ar) {
+ & p, & h1, & h2, & h3, article, header {
+ direction: rtl;
+ text-align: right;
+ }
+ }
+
+ & footer > p {
+ text-align: center !important;
+ }
+ }
+
+ .singlePage {
+ padding: 4em 30vw;
+ @media all and (max-width: 1200px) {
+ padding: 25px 5vw;
+ }
+ }
+
+
+ body {
+ margin: 0;
+ height: 100vh;
+ width: 100vw;
+ max-width: 100%;
+ box-sizing: border-box;
+ background-color: var(--light);
+ }
+
+ h1, h2, h3, h4, h5, h6, thead {
+ font-family: var(--font-header);
+ color: var(--dark);
+ font-weight: revert;
+ margin: 2rem 0 0;
+ padding: 2rem 0 1rem;
+
+ &:hover > .hanchor {
+ color: var(--secondary);
+ }
+ }
+
+ .hanchor {
+ font-family: var(--font-header);
+ opacity: 0.8;
+ transition: color 0.3s ease;
+ color: var(--dark);
+ }
+
+ p, ul, text, a, tr, td, li, ol, ul {
+ font-family: var(--font-body);
+ color: var(--gray);
+ fill: var(--gray);
+ font-weight: revert;
+ margin: revert;
+ padding: revert;
+ }
+
+ tbody, li, p {
+ line-height: 1.5em;
+ }
+
+ .mainTOC {
+ border-radius: 5px;
+ padding: 0.75em 0;
+
+ & details {
+ & summary {
+ cursor: zoom-in;
+ font-family: var(--font-header);
+ color: var(--dark);
+ font-weight: 700;
+ }
+
+ &[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: var(--font-body);
+ font-weight: 700;
+ }
+ }
+
+ table {
+ border: 1px solid var(--outlinegray);
+ width: 100%;
+ padding: 1.5em;
+ border-collapse: collapse;
+ }
+
+ td, th {
+ padding: 0.2em 1em;
+ border: 1px solid var(--outlinegray);
+ }
+
+ img {
+ max-width: 100%;
+ border-radius: 3px;
+ margin: 1em 0;
+ }
+
+ p > img + em {
+ display: block;
+ transform: translateY(-1em);
+ }
+
+ sup {
+ line-height: 0
+ }
+
+ blockquote {
+ margin-left: 0em;
+ border-left: 3px solid var(--secondary);
+ padding-left: 1em;
+ transition: border-color 0.2s ease;
+ }
+
+ .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$="#"], &.active a {
+ opacity: 0.2;
+ }
+ }
+ }
+
+ article {
+ & > h1 {
+ margin-top: 2em;
+ font-size: 2em;
+ }
+
+ & > .meta {
+ //margin: 1.5em 0 1em 0;
+ margin: 0 0 1em 0;
+ opacity: 0.7;
+ }
+
+ & a {
+ 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;
+ }
+ }
+
+ .tags {
+ list-style: none;
+ padding-left: 0;
+
+ & .meta {
+ margin: 1.5em 0;
+
+ & > h1 {
+ margin: 0;
+ }
+
+ & > p {
+ margin: 0;
+ }
+ }
+
+ & > li {
+ display: inline-block;
+ margin: 0.4em 0.2em;
+ }
+
+ & > li > a {
+ border-radius: 8px;
+ border: var(--outlinegray) 1px solid;
+ padding: 0.2em 0.5em;
+
+ &::before {
+ content: "#";
+ margin-right: 0.3em;
+ color: var(--outlinegray);
+ }
+ }
+ }
+
+ .backlinks a {
+ font-weight: 600;
+ font-size: 0.9rem;
+ }
+
+ sup > a {
+ text-decoration: none;
+ padding: 0 0.1em 0 0.2em;
+ }
+
+ #page-title {
+ margin: 0;
+
+ & > a {
+ font-family: var(--font-header);
+ }
+ }
+
+ a {
+ 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: var(--font-mono);
+ padding: 0.75em;
+ border-radius: 3px;
+ overflow-x: scroll;
+ }
+
+ code {
+ font-family: var(--font-mono);
+ font-size: 0.85em;
+ padding: 0.15em 0.3em;
+ border-radius: 5px;
+ background: var(--lightgray);
+ }
+
+ @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);
+ }
+
+ .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: 0;
+ margin: 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;
+ margin: 0.5em 0;
+
+ & > svg {
+ margin-bottom: -5px;
+
+ }
+ }
+ }
+
+ .centered {
+ margin-top: 30vh;
+ }
+
+ .spacer {
+ flex: 1 1 auto;
+ }
+
+ header {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ margin: 1em 0 2em;
+
+ & > h1 {
+ font-size: 2em;
+ }
+
+ & > nav {
+ @media all and (max-width: 600px) {
+ display: none;
+ }
+ }
+
+ #search-icon {
+ background-color: var(--lightgray);
+ border-radius: 4px;
+ height: 2em;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+
+ & > p {
+ display: inline;
+ padding: 0 1.5em 0 2em;
+ }
+ }
+
+ & svg {
+ cursor: pointer;
+ width: 18px;
+ min-width: 18px;
+ margin: 0 0.5em;
+
+ &: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: var(--font-body);
+ 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;
+ margin-top: 2em;
+ padding-left: 0;
+
+ }
+
+ .section-li {
+ margin-bottom: 1em;
+
+ & > .section {
+ display: flex;
+ align-items: center;
+
+ @media all and (max-width: 600px) {
+ & .tags {
+ display: none;
+ }
+ }
+
+ & h3 > a {
+ font-weight: 700;
+ margin: 0;
+ }
+
+ & p {
+ margin: 0;
+ padding-right: 1em;
+ flex-basis: 6em;
+ }
+ }
+
+ & h3 {
+ opacity: 1;
+ font-weight: 700;
+ margin: 0em;
+ }
+
+ & .meta {
+ opacity: 0.6;
+ }
+ }
+
+ @keyframes dropin {
+ 0% {
+ display: none;
+ opacity: 0;
+ visibility: hidden;
+ }
+ 1% {
+ display: inline-block;
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ visibility: visible;
+ }
+ }
+
+ .popover {
+ z-index: 999;
+ position: absolute;
+ width: 20rem;
+ display: none;
+ background-color: var(--light);
+ padding: 1rem;
+ margin: 1rem;
+ border: 1px solid var(--outlinegray);
+ border-radius: 5px;
+ 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;
+ display: inline-block;
+ animation: dropin 0.2s ease;
+ }
+
+ & > h3 {
+ font-size: 1rem;
+ margin: 0.25rem 0;
+ }
+
+ & .meta {
+ margin-top: 0.25rem;
+ opacity: 0.5;
+ font-family: var(--font-mono);
+ font-size: 0.8rem;
+ }
+
+ & > p {
+ margin: 0;
+ padding: 0.5rem 0;
+ }
+
+ & > p, & > a {
+ font-size: 1rem;
+ 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/clipboard.scss b/assets/styles/clipboard.scss
new file mode 100644
index 000000000..7989e248c
--- /dev/null
+++ b/assets/styles/clipboard.scss
@@ -0,0 +1,47 @@
+.clipboard-button {
+ position: absolute;
+ display: flex;
+ float: right;
+ right: 0;
+ padding: 0.69em;
+ margin: 0.5em;
+ color: var(--outlinegray);
+ border-color: var(--dark);
+ background-color: var(--lightgray);
+ filter: contrast(1.1);
+ border: 2px solid;
+ border-radius: 6px;
+ font-size: 0.8em;
+ z-index: 1;
+ opacity: 0;
+ transition: 0.12s;
+
+ & > svg {
+ fill: var(--light);
+ filter: contrast(0.3);
+ }
+
+ &:hover {
+ cursor: pointer;
+ border-color: var(--primary);
+
+ & > svg {
+ fill: var(--primary);
+ }
+ }
+
+ &:focus {
+ outline: 0;
+ }
+}
+
+.highlight {
+ position: relative;
+
+ &:hover > .clipboard-button {
+ opacity: 1;
+ transition: 0.2s;
+ }
+}
+
+
diff --git a/assets/styles/code-title.scss b/assets/styles/code-title.scss
new file mode 100644
index 000000000..b384743b8
--- /dev/null
+++ b/assets/styles/code-title.scss
@@ -0,0 +1,20 @@
+.code-title {
+ color: var(--primary) ;
+ font-family: var(--font-mono);
+ width: max-content;
+ overflow-x: auto;
+ display: inline-block;
+ vertical-align: middle;
+ font-weight: normal;
+ line-height: 1em;
+ position: relative;
+ padding: 0.5em 0.6em 0.6em; // + 1.2 em
+ max-width: calc(100% - 1.2em); // (-1.2 em) fits article width exactly
+ margin-bottom: -0.2em;
+ z-index: -1;
+ border-top-left-radius: 0.3em;
+ border-top-right-radius: 0.3em;
+ font-size: 0.9em;
+ background-color: var(--lightgray);
+ filter: hue-rotate(-30deg) contrast(1.0) opacity(0.8);
+}
\ No newline at end of file
diff --git a/assets/styles/custom.scss b/assets/styles/custom.scss
new file mode 100644
index 000000000..ac7d23a64
--- /dev/null
+++ b/assets/styles/custom.scss
@@ -0,0 +1,284 @@
+// Add your own CSS here!
+
+:root {
+ --light: #1e1e21 !important;
+ --dark: #fbfffe !important;
+ --secondary: #6b879a !important;
+ --visited: #4a575e !important;
+ --tertiary: #84a59d !important;
+ --primary: #f58382 !important;
+ --gray: #d4d4d4 !important;
+ --lightgray: #292633 !important;
+ --outlinegray: #343434 !important;
+}
+
+[saved-theme="dark"] {
+ --light: #faf8f8;
+ --dark: #141021;
+ --secondary: #284b63;
+ --tertiary: #84a59d;
+ --visited: #afbfc9;
+ --primary: #f28482;
+ --gray: #4e4e4e;
+ --lightgray: #f0f0f0;
+ --outlinegray: #dadada;
+ --million-progress-bar-color: var(--secondary);
+}
+
+:root {
+ --text-accent: rgb(238, 27, 231);
+ --background-primary-alt: #000;
+}
+.theme-dark {
+ --text-accent: rgb(238, 27, 231);
+ --background-primary-alt: #000;
+}
+
+html {
+ font-size: 24px;
+ font-family: "Poppins", sans-serif;
+}
+
+body, body.theme-dark {
+ color: #fff;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ font-size: .75rem;
+ background: #111;
+ box-sizing: border-box;
+ font-family: "Poppins", sans-serif;
+ font-weight: 500;
+ line-height: 1em;
+ font-smoothing: antialiased;
+ text-size-adjust: 100%;
+ -ms-text-size-adjust: 100%;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+#page-title a { display: flex; align-items: center; color: #fff; font-size: .75rem; }
+#page-title img { margin-right: 15px; }
+
+.mainTOC { background: #222; }
+
+article a {
+ color: rgb(238, 27, 231); background: none;
+}
+a:hover {
+ color: #ee1be7 !important; text-decoration: underline !important;
+}
+article a.internal-link { background: none; }
+
+article h2 {
+ margin-top: 2rem; font-size: 1.35rem;
+}
+
+p, article, article p, article li, article a, .page-end>.backlinks-container>ul>li {
+ font-family: "Poppins", sans-serif;
+ line-height: 29px;
+ font-size: 16px;
+ font-weight: 500;
+}
+
+#TableOfContents>ol>li::marker {
+ font-family: "Poppins", sans-serif;
+}
+
+header { justify-content: space-between; }
+header>.spacer { display: none; }
+
+.mainTOC {
+ margin: 1rem 0; cursor: pointer;
+}
+.mainTOC details summary, .mainTOC details[open] summary {
+ color: #888; cursor: pointer;
+}
+
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, ol a, ul a, thead a {
+ font-family: "Poppins", sans-serif;
+ font-weight: revert;
+ margin: revert;
+ padding: revert;
+ font-size: inherit;
+}
+
+body.theme-dark h1,
+body.theme-dark h2,
+body.theme-dark h3,
+body.theme-dark h4,
+body.theme-dark h5,
+body.theme-dark h6,
+body.theme-dark .page-header {
+ font-family: 'Playfair Display', serif;
+ margin-top: 45px;
+ margin-bottom: 25px;
+}
+
+.page-header {
+ font-size: 2.5rem;
+}
+
+.markdown-preview-view h1 {
+ font-size: 32px;
+ font-family: 'Playfair Display', sans-serif;
+ font-weight: 500;
+ line-height: 1.5;
+}
+
+.markdown-preview-view {
+ font-size: 16px;
+ line-height: 26px;
+ font-family: 'Poppins', sans-serif;
+}
+
+.markdown-preview-view .internal-link.is-unresolved {
+ opacity: .75;
+}
+.published-container.is-readable-line-width .site-body {
+ justify-content: initial;
+}
+
+.site-body-left-column-site-name {
+ padding-left: 60px;
+ position: relative;
+}
+
+.site-body-left-column {
+ flex: 0 0 330px;
+}
+
+.published-container.is-readable-line-width .site-body-center-column {
+ flex: 0 1 940px;
+}
+
+.site-body-left-column-site-name:before {
+ content: '';
+ display: block;
+ width: 30px;
+ height: 30px;
+ background: url(https://overment.com/images/logo-icon.svg);
+ background-size: cover;
+ position: absolute;
+ left: 20px;
+ top: 11px;
+}
+
+.has-navigation .site-body-left-column {
+ background: #000;
+}
+
+img[alt="overment"] { width: 200px; margin: 0 auto; display: block; }
+img[alt="avatar"] { width: 150px; display: block; }
+.logo { width: 50px; margin: 0; }
+
+
+@media (max-width: 1024px) {
+ .site-body-left-column-site-name:before {
+ top: 0;
+ }
+ body.theme-dark {
+ font-size: 1.2rem;
+ }
+
+ h1, h2, h3, h4, h5, h6, {
+ margin-top: 25px;
+ }
+
+ .markdown-preview-view {
+ font-size: 1.3rem;
+ line-height: 1.7rem;
+ }
+}
+
+.newsletter-btn {
+ background: #232323;
+ color: #ff40ff;
+ font-weight: bold;
+ border-radius: 5px;
+ width: 115px;
+ height: 50px;
+ position: fixed;
+ right: 20px;
+ bottom: 20px;
+ border: 1px solid #ff40ff;
+ cursor: pointer;
+ transition: .2s all ease-in-out;
+}
+
+.newsletter-btn:hover {
+ transform: scale(1.1);
+}
+
+[saved-theme="dark"] {
+ --light: #1e1e21 !important;
+ --dark: #fbfffe !important;
+ --secondary: #6b879a !important;
+ --visited: #4a575e !important;
+ --tertiary: #84a59d !important;
+ --primary: #f58382 !important;
+ --gray: #d4d4d4 !important;
+ --lightgray: #292633 !important;
+ --outlinegray: #343434 !important;
+}
+
+
+
+.tree,
+.tree ol {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+}
+
+.tree ol {
+ padding-left: 1em;
+ padding-top: 5px;
+}
+.tree summary {
+ color: #fff;
+ font-size: 14px;
+}
+.tree a{
+ font-weight:unset;
+ font-size: 14px;
+ color: #fff;
+}
+
+a.active{
+ color: #ff40ff;
+}
+
+body {
+ display: flex;
+}
+
+@media (max-width: 768px) {
+ .menu {
+ display: none !important;
+ }
+}
+
+.menu {
+ background: #000;
+ padding: 25px 35px 0 0;
+ min-width: 250px;
+ max-width: 250px;
+ margin-right: 40px;
+ flex: 1;
+}
+
+.menu details {
+ padding: 5px 0;
+}
+
+.singlePage {
+ padding-left: 0;
+}
+
+.singlePage img {
+ max-width: 500px;
+ display: block;
+}
diff --git a/assets/styles/syntax.scss b/assets/styles/syntax.scss
new file mode 100644
index 000000000..6267f5cf3
--- /dev/null
+++ b/assets/styles/syntax.scss
@@ -0,0 +1,66 @@
+// Overrides
+/* Background */
+.chroma {
+ overflow: hidden !important;
+ background-color: var(--lightgray) !important;
+}
+
+/* LineTable */
+.chroma .lntable {
+ width: auto !important;
+ overflow: auto !important;
+ display: block !important;
+}
+
+/* LineHighlight */
+.chroma .hl {
+ display: block !important;
+ width: 100% !important;
+}
+
+/* LineNumbersTable */
+.chroma .lnt {
+ margin-right: 0.0em !important;
+ padding: 0 0.0em 0 0.0em !important;
+}
+
+/* LineNumbers */
+.chroma .ln {
+ margin-right: 0.0em !important;
+ padding: 0 0.0em 0 0.0em !important;
+}
+
+/* GenericDeleted */
+.chroma .gd {
+ color: #8b080b !important;
+}
+
+/* GenericInserted */
+.chroma .gi {
+ font-weight: bold !important;
+}
+
+.lntd:first-of-type > .chroma {
+ padding-right: 0 !important;
+}
+
+.chroma code {
+ font-family: var(--font-mono) !important;
+ font-size: 0.85em !important;
+ line-height: 2em !important;
+ background: none !important;
+ padding: 0 !important;
+}
+
+.chroma {
+ border-radius: 3px !important;
+ margin: 0 !important;
+}
+
+pre.chroma {
+ -moz-tab-size:4;-o-tab-size:4;tab-size:4;
+}
+
+.katex {
+ font-size: 1.1em !important;
+}
diff --git a/config.toml b/config.toml
new file mode 100644
index 000000000..3336117ea
--- /dev/null
+++ b/config.toml
@@ -0,0 +1,38 @@
+baseURL = "https://brain.overment.com/"
+languageCode = "en-us"
+pygmentsUseClasses = true
+googleAnalytics = "G-XYFD95KB4J"
+relativeURLs = false
+disablePathToLower = true
+ignoreFiles = [
+ "/content/templates/*",
+ "/content/private/*",
+]
+summaryLength = 20
+paginate = 10
+enableGitInfo = true
+pluralizelisttitles = false
+
+[markup]
+ [markup.goldmark.renderer]
+ unsafe= true
+ [markup.tableOfContents]
+ endLevel = 3
+ ordered = true
+ startLevel = 2
+ [markup.highlight]
+ noClasses = false
+ anchorLineNos = false
+ codeFences = true
+ guessSyntax = true
+ hl_Lines = ""
+ lineAnchors = ""
+ lineNoStart = 1
+ lineNos = true
+ lineNumbersInTable = true
+ style = "dracula"
+ [frontmatter]
+ lastmod = ["lastmod", ":git", "date", "publishDate"]
+ publishDate = ["publishDate", "date"]
+ [markup.goldmark.renderer]
+ unsafe = true
diff --git a/content/_index.md b/content/_index.md
index 69dcc4c1c..acea292c3 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -31,3 +31,4 @@ You can search all the information contained here with the help of the search en
This entire project is based on the concept of the so-called [digital garden](https://joelhooks.com/digital-garden). In my case, I've been testing a combination of Notion and Super.so and Github integration with GitBook. For a while I used the Obsidian Publish function and currently I generate the whole thing in Hugo based on the Quartz project [Quartz](https://quartz.jzhao.xyz/)
> **Important!** If you find a mistake in some definitions or want to suggest a change, I will be very grateful for it. You can do this with a Pull Request sent to the [of this repository](https://github.com/iceener/brain), [reporting Issue](https://github.com/iceener/brain/issues/new) or simply by emailing me at [e-mail](mailto:adam@overment.com).
+
diff --git a/data/config.yaml b/data/config.yaml
new file mode 100644
index 000000000..5b27779fc
--- /dev/null
+++ b/data/config.yaml
@@ -0,0 +1,26 @@
+name: Adam Gospodarczyk
+enableToc: false
+aopenToc: false
+enableLinkPreview: false
+enableLatex: false
+openToc: false
+enableCodeBlockTitle: true
+enableCodeBlockCopy: true
+enableCallouts: true
+enableSPA: true
+enableFooter: true
+enableContextualBacklinks: true
+enableRecentNotes: false
+enableGitHubEdit: true
+GitHubLink: https://github.com/jackyzha0/quartz/tree/hugo/content
+enableSemanticSearch: false
+operandApiKey: "REPLACE-WITH-YOUR-OPERAND-API-KEY"
+description:
+ This is where I share everything I know with you. I created it primarily for myself, so that I can easily return to all the material posted here. At the same time, I thought you might find it all equally valuable to you.
+page_title:
+ "brain.overment.com"
+links:
+ - link_name: Twitter
+ link: https://twitter.com/_overment
+ - link_name: Github
+ link: https://github.com/iceener
diff --git a/data/graphConfig.yaml b/data/graphConfig.yaml
new file mode 100644
index 000000000..a6f916acb
--- /dev/null
+++ b/data/graphConfig.yaml
@@ -0,0 +1,37 @@
+# if true, a Global Graph will be shown on home page with full width, no backlink.
+# A different set of Local Graphs will be shown on sub pages.
+# if false, Local Graph will be default on every page as usual
+enableGlobalGraph: false
+
+### Local Graph ###
+
+localGraph:
+ enableLegend: false
+ enableDrag: true
+ enableZoom: true
+ depth: 1 # set to -1 to show full graph
+ scale: 1.2
+ repelForce: 2
+ centerForce: 1
+ linkDistance: 1
+ fontSize: 0.6
+ opacityScale: 3
+
+### Global Graph ###
+
+globalGraph:
+ enableLegend: false
+ enableDrag: true
+ enableZoom: true
+ depth: -1 # set to -1 to show full graph
+ scale: 1.4
+ repelForce: 1
+ centerForce: 1
+ linkDistance: 1
+ fontSize: 0.5
+ opacityScale: 3
+
+### For all graphs ###
+
+paths:
+ - /moc: "#4388cc"
diff --git a/i18n/ar.toml b/i18n/ar.toml
new file mode 100644
index 000000000..631c07155
--- /dev/null
+++ b/i18n/ar.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "يبدو أنك ضللت الطريق. هذه الصفحة غير موجودة (أو قد تكون خاصة)."
+
+[404_back]
+other = "↳ العودة للرئيسية."
+
+[all_posts]
+other = "كل منشورات {{.Title}}"
+
+[last_updated]
+other = "آخر تعديل"
+
+[notes_count]
+other = "ملاحظات بهذه التسمية"
+
+[first_10]
+other = "(تعرض أول 10 نتائج فقط)"
+
+[tag]
+other = "التسمية"
+
+[backlinks]
+other = "الروابط الخلفية"
+
+[no_backlinks]
+other = "لا توجد روابط خلفية"
+
+[home]
+other = "الرئيسية"
+
+[light_mode]
+other = "السمة الفاتحة"
+
+[dark_mode]
+other = "السمة الداكنة"
+
+[edit_source]
+other = "تعديل المصدر"
+
+[interactive_graph]
+other = "المخطط التفاعلي"
+
+[search]
+other = "البحث"
+
+[search_icon]
+other = "أيقونة البحث"
+
+[icon_search]
+other = "أيقونة فتح نافذة البحث"
+
+[recent_notes]
+other = "الملاحظات اﻷخيرة"
+
+[first_3_notes]
+other = "أول 3 {{ .notes }}"
+
+[search_for_something]
+other = "ابحث عن شيء ما..."
+
+[toc]
+other = "الفهرس"
+
+[copyright]
+other = "صُمم بواسطة {{ .name }} باستخدام كوارتز، {{ .year }} ©"
diff --git a/i18n/de.toml b/i18n/de.toml
new file mode 100644
index 000000000..266bfd71c
--- /dev/null
+++ b/i18n/de.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "Hey! Hast du dich verirrt? Diese Seite existiert leider nicht (oder ist privat)."
+
+[404_back]
+other = "↳ Zurück zur Startseite."
+
+[all_posts]
+other = "Alle {{.Title}}"
+
+[last_updated]
+other = "Zuletzt aktualisiert"
+
+[notes_count]
+other = "Beiträge mit diesem Tag"
+
+[first_10]
+other = "Zeige die ersten 10 Ergebnisse"
+
+[tag]
+other = "Tag"
+
+[backlinks]
+other = "Backlinks"
+
+[no_backlinks]
+other = "Keine Backlinks gefunden"
+
+[home]
+other = "Home"
+
+[light_mode]
+other = "Light Mode"
+
+[dark_mode]
+other = "Dark Mode"
+
+[edit_source]
+other = "Quelldatei bearbeiten"
+
+[interactive_graph]
+other = "Interaktiver Graph"
+
+[search]
+other = "Suche"
+
+[search_icon]
+other = "Suchsymbol"
+
+[icon_search]
+other = "Symbol, um die Suche zu öffnen"
+
+[recent_notes]
+other = "Neuste Beiträge"
+
+[first_3_notes]
+other = "Die ersten 3 {{ .notes }}"
+
+[search_for_something]
+other = "Suche nach etwas ..."
+
+[toc]
+other = "Inhaltsverzeichnis"
+
+[copyright]
+other = "Made by {{ .name }} using Quartz, © {{ .year }}"
diff --git a/i18n/en.toml b/i18n/en.toml
new file mode 100644
index 000000000..b9ce33e6c
--- /dev/null
+++ b/i18n/en.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "Hey! You look a little lost. This page doesn't exist (or may be private)."
+
+[404_back]
+other = "↳ Let's get you home."
+
+[all_posts]
+other = "All {{.Title}}"
+
+[last_updated]
+other = "Last updated"
+
+[notes_count]
+other = "notes with this tag"
+
+[first_10]
+other = "showing first 10 results"
+
+[tag]
+other = "Tag"
+
+[backlinks]
+other = "Backlinks"
+
+[no_backlinks]
+other = "No backlinks found"
+
+[home]
+other = "Home"
+
+[light_mode]
+other = "Light Mode"
+
+[dark_mode]
+other = "Dark Mode"
+
+[edit_source]
+other = "Edit Source"
+
+[interactive_graph]
+other = "Interactive Graph"
+
+[search]
+other = "Search"
+
+[search_icon]
+other = "Search Icon"
+
+[icon_search]
+other = "Icon to open search"
+
+[recent_notes]
+other = "Recent Notes"
+
+[first_3_notes]
+other = "first 3 {{ .notes }}"
+
+[search_for_something]
+other = "Search for something..."
+
+[toc]
+other = "Table of Contents"
+
+[copyright]
+other = "Made by {{ .name }} using Quartz, © {{ .year }}"
diff --git a/i18n/es.toml b/i18n/es.toml
new file mode 100644
index 000000000..2effa2371
--- /dev/null
+++ b/i18n/es.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "Hey! Te ves un poco perdido. Esta página no existe (o puede que sea privada)."
+
+[404_back]
+other = "↳ Vamos a llevarte de regreso a casa."
+
+[all_posts]
+other = "Todos {{.Title}}"
+
+[last_updated]
+other = "Actualizado por última vez"
+
+[notes_count]
+other = "notas con esta etiqueta"
+
+[first_10]
+other = "mostrando los primeros 10 resultados"
+
+[tag]
+other = "Etiqueta"
+
+[backlinks]
+other = "Backlinks"
+
+[no_backlinks]
+other = "No se encontraron backlinks"
+
+[home]
+other = "Casa"
+
+[light_mode]
+other = "Modo Claro"
+
+[dark_mode]
+other = "Modo Oscuro"
+
+[edit_source]
+other = "Editar Fuente"
+
+[interactive_graph]
+other = "Gráfico Interactivo"
+
+[search]
+other = "Búsqueda"
+
+[search_icon]
+other = "Ícono de Búsqueda"
+
+[icon_search]
+other = "Ícono para abrir la búsqueda"
+
+[recent_notes]
+other = "Notas Recientes"
+
+[first_3_notes]
+other = "primeras 3 {{ .notes }}"
+
+[search_for_something]
+other = "Buscar algo..."
+
+[toc]
+other = "Tabla de Contenido"
+
+[copyright]
+other = "Hecho por {{ .name }} usando Quartz, © {{ .year }}"
diff --git a/i18n/fr.toml b/i18n/fr.toml
new file mode 100644
index 000000000..e87008d54
--- /dev/null
+++ b/i18n/fr.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "Hey ! Vous semblez perdu‧e. Cette page n'existe pas (ou est privée)."
+
+[404_back]
+other = "↳ On va te faire retourner à l'accueil"
+
+[all_posts]
+other = "Tout {{.Title}}"
+
+[last_updated]
+other = "Dernière modification"
+
+[notes_count]
+other = "notes avec ce tag"
+
+[first_10]
+other = "affichant les 10 premiers résultats"
+
+[tag]
+other = "Tag"
+
+[backlinks]
+other = "Backlinks"
+
+[no_backlinks]
+other = "Pas de backlinks trouvés"
+
+[home]
+other = "Accueil"
+
+[light_mode]
+other = "Mode Clair"
+
+[dark_mode]
+other = "Mode Sombre"
+
+[edit_source]
+other = "Editer la source"
+
+[interactive_graph]
+other = "Graphique interactif"
+
+[search]
+other = "Rechercher"
+
+[search_icon]
+other = "Icône de recherche"
+
+[icon_search]
+other = "Icon pour ouvrir la recherche"
+
+[recent_notes]
+other = "Notes récentes"
+
+[first_3_notes]
+other = "3 premières {{ .notes }}"
+
+[search_for_something]
+other = "Rechercher quelque-chose..."
+
+[toc]
+other = "Table des matières"
+
+[copyright]
+other = "Fait par {{ .name }} en utilisant Quartz, © {{ .year }}"
diff --git a/i18n/uk.toml b/i18n/uk.toml
new file mode 100644
index 000000000..f84f5ce01
--- /dev/null
+++ b/i18n/uk.toml
@@ -0,0 +1,65 @@
+[404_message]
+other = "Хей! Виглядаєте здивовано. Цієї сторінки не існує (або вона приватна)."
+
+[404_back]
+other = "↳ Повернемося додому."
+
+[all_posts]
+other = "Всі {{.Title}}"
+
+[last_updated]
+other = "Оновлено"
+
+[notes_count]
+other = "нонаток з цим тегом"
+
+[first_10]
+other = "показано 10 перших результатів"
+
+[tag]
+other = "Тег"
+
+[backlinks]
+other = "Зворотнє посилання"
+
+[no_backlinks]
+other = "Зворотних посилань не знайдено"
+
+[home]
+other = "Дім"
+
+[light_mode]
+other = "Світлий Режим"
+
+[dark_mode]
+other = "Темний Режим"
+
+[edit_source]
+other = "Редагувати Джерело"
+
+[interactive_graph]
+other = "Інтерактивний граф"
+
+[search]
+other = "Пошук"
+
+[search_icon]
+other = "Іконка Пошуку"
+
+[icon_search]
+other = "Іконка для відкриття пошуку"
+
+[recent_notes]
+other = "Нещодавні Нотатки"
+
+[first_3_notes]
+other = "перші 3 {{ .notes }}"
+
+[search_for_something]
+other = "Знайти щось..."
+
+[toc]
+other = "Зміст"
+
+[copyright]
+other = "Створено {{ .name }} з використанням Quartz, © {{ .year }}"
diff --git a/layouts/404.html b/layouts/404.html
new file mode 100644
index 000000000..1eb60900f
--- /dev/null
+++ b/layouts/404.html
@@ -0,0 +1,14 @@
+
+
+{{ partial "head.html" . }}
+
+
+
+
+
diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html
new file mode 100644
index 000000000..dbcf732fd
--- /dev/null
+++ b/layouts/_default/_markup/render-image.html
@@ -0,0 +1,9 @@
+{{$src := .Destination | safeURL }}
+{{$width := index (split .Text "|") 1 | default "auto" }}
+{{$external := strings.HasPrefix $src "http" }}
+{{- if $external -}}
+
+{{- else -}}
+{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}}
+
+{{- end -}}
diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html
new file mode 100644
index 000000000..4757b7284
--- /dev/null
+++ b/layouts/_default/_markup/render-link.html
@@ -0,0 +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 ((.Page.GetPage $spacedurl).RelPermalink) }}
+{{- .Text | safeHTML -}}
+
+{{- end -}}
diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html
new file mode 100644
index 000000000..8754b1f28
--- /dev/null
+++ b/layouts/_default/baseof.html
@@ -0,0 +1,10 @@
+
+
+{{ block "head" . }}
+{{ end }}
+
+
+{{ block "main" . }}
+{{ end }}
+
+
\ No newline at end of file
diff --git a/layouts/_default/section.html b/layouts/_default/section.html
new file mode 100644
index 000000000..e4d5b319e
--- /dev/null
+++ b/layouts/_default/section.html
@@ -0,0 +1,25 @@
+
+
+{{ partial "head.html" . }}
+
+
+{{partial "search.html" .}}
+
+
+
+
+
+
+
+
+ {{ i18n "all_posts" . }}
+ {{with .Params.description}}
+ {{.}}
+ {{end}}
+ {{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
new file mode 100644
index 000000000..b4c21bd6a
--- /dev/null
+++ b/layouts/_default/single.html
@@ -0,0 +1,40 @@
+
+
+{{ partial "head.html" . }}
+
+
+{{partial "search.html" .}}
+
+
+
+
+
+
+
+
+
+
+ {{if .Title}}{{ .Title }}
{{end}}
+
+ Last update: {{if ne .Date .Lastmod}} {{ .Lastmod.Format "02/01/2006" }}{{else}} nieznana {{end}}
+
+
+ {{partial "toc.html" .}}
+ {{partial "textprocessing.html" . }}
+
+ {{partial "footer.html" .}}
+ {{partial "popover.html" .}}
+
+
+
+
diff --git a/layouts/_default/taxonomy.html b/layouts/_default/taxonomy.html
new file mode 100644
index 000000000..bf28139d3
--- /dev/null
+++ b/layouts/_default/taxonomy.html
@@ -0,0 +1,34 @@
+
+
+{{ partial "head.html" . }}
+
+
+{{partial "search.html" .}}
+
+
+
+
+
+
+
+
+ {{ i18n "all_posts" . }}
+ {{with .Params.description}}
+ {{.}}
+ {{end}}
+
+
+ {{partial "contact.html" .}}
+
+
+
diff --git a/layouts/_default/term.html b/layouts/_default/term.html
new file mode 100644
index 000000000..dbc0753b7
--- /dev/null
+++ b/layouts/_default/term.html
@@ -0,0 +1,25 @@
+
+
+{{ partial "head.html" . }}
+
+
+{{partial "search.html" .}}
+
+
+
+
+
+
+
+
+ {{ i18n "tag" }}: {{ .Title }}
+ {{with .Params.description}}
+ {{.}}
+ {{end}}
+ {{partial "page-list.html" .Paginator.Pages}}
+ {{ template "_internal/pagination.html" . }}
+
+ {{partial "contact.html" .}}
+
+
+
diff --git a/layouts/index.html b/layouts/index.html
new file mode 100644
index 000000000..721f881c7
--- /dev/null
+++ b/layouts/index.html
@@ -0,0 +1,30 @@
+
+
+{{ partial "head.html" . }}
+
+{{ partial "search.html" .}}
+
+
+
+
+ {{if .Title}}{{ .Title }}{{else}}Untitled{{end}}
+
+
+
+
+ {{partial "toc.html" .}}
+ {{partial "textprocessing.html" . }}
+ {{if $.Site.Data.config.enableRecentNotes}}
+ {{partial "recent.html" . }}
+ {{end}}
+
+ {{partial "footerIndex.html" .}}
+
+
+
diff --git a/layouts/partials/backlinks.html b/layouts/partials/backlinks.html
new file mode 100644
index 000000000..2f804c9fa
--- /dev/null
+++ b/layouts/partials/backlinks.html
@@ -0,0 +1,31 @@
+Powiązania
+{{ i18n "backlinks" }}
+
+ {{$url := urls.Parse .Site.BaseURL }}
+ {{$host := strings.TrimRight "/" $url.Path }}
+ {{$curPage := strings.TrimPrefix $host (strings.TrimRight "/" .Page.RelPermalink)}}
+ {{$linkIndex := getJSON "/assets/indices/linkIndex.json"}}
+ {{$inbound := index $linkIndex.index.backlinks $curPage}}
+ {{$contentTable := getJSON "/assets/indices/contentIndex.json"}}
+ {{if $inbound}}
+ {{$backlinks := dict "SENTINEL" "SENTINEL"}}
+ {{range $k, $v := $inbound}}
+ {{$cleanedInbound := replace $v.source " " "-"}}
+ {{$ctx := $v.text}}
+ {{$backlinks = merge $backlinks (dict $cleanedInbound $ctx)}}
+ {{end}}
+ {{- range $lnk, $ctx := $backlinks -}}
+ {{$l := printf "%s%s/" $host $lnk}}
+ {{$l = cond (eq $l "//") "/" $l}}
+ {{with (index $contentTable $lnk)}}
+ -
+ {{index (index . "title")}}
+
+ {{end}}
+ {{- end -}}
+ {{else}}
+ -
+ {{ i18n "no_backlinks" }}
+ layouts/partials/footer.html
+ {{end}}
+
diff --git a/layouts/partials/contact.html b/layouts/partials/contact.html
new file mode 100644
index 000000000..3de7d2c68
--- /dev/null
+++ b/layouts/partials/contact.html
@@ -0,0 +1,19 @@
+
+{{ $config := cond (eq $.Site.Language.Lang "en") "config" (printf "config.%s" $.Site.Language.Lang) }}
+{{ $data := index $.Site.Data $config }}
+
+
diff --git a/layouts/partials/darkmode.html b/layouts/partials/darkmode.html
new file mode 100644
index 000000000..3aa7175b6
--- /dev/null
+++ b/layouts/partials/darkmode.html
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/layouts/partials/date-fmt.html b/layouts/partials/date-fmt.html
new file mode 100644
index 000000000..6104226e2
--- /dev/null
+++ b/layouts/partials/date-fmt.html
@@ -0,0 +1,7 @@
+{{if .Date}}
+{{.Date.Format "Jan 2, 2006"}}
+{{else if .Lastmod}}
+{{.Lastmod.Format "Jan 2, 2006"}}
+{{else}}
+Unknown
+{{end}}
diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html
new file mode 100644
index 000000000..8ad23d5bf
--- /dev/null
+++ b/layouts/partials/footer.html
@@ -0,0 +1,44 @@
+
+
+
+
+{{if $.Site.Data.config.enableFooter}}
+
+{{end}}
+
+
+ {{partial "contact.html" .}}
+
+ {{partial "newsletter.html" .}}
+
+
+{{partial "contact.html" .}}
diff --git a/layouts/partials/footerIndex.html b/layouts/partials/footerIndex.html
new file mode 100644
index 000000000..5b73fa45d
--- /dev/null
+++ b/layouts/partials/footerIndex.html
@@ -0,0 +1,24 @@
+{{if $.Site.Data.config.enableFooter}}
+ {{if $.Site.Data.graphConfig.enableGlobalGraph}}
+
+ {{else}}
+
+
+ {{end}}
+{{end}}
+
+{{partial "contact.html" .}}
diff --git a/layouts/partials/github.html b/layouts/partials/github.html
new file mode 100644
index 000000000..836dc79cb
--- /dev/null
+++ b/layouts/partials/github.html
@@ -0,0 +1,3 @@
+{{if $.Site.Data.config.enableGitHubEdit}}
+{{ i18n "edit_source" }}
+{{end}}
diff --git a/layouts/partials/graph.html b/layouts/partials/graph.html
new file mode 100644
index 000000000..3888b1d17
--- /dev/null
+++ b/layouts/partials/graph.html
@@ -0,0 +1,19 @@
+
+
+{{ i18n "interactive_graph" }}
+
+
+{{ $js := resources.Get "js/graph.js" | resources.Fingerprint "md5" }}
+
diff --git a/layouts/partials/head.html b/layouts/partials/head.html
new file mode 100644
index 000000000..19e965ee3
--- /dev/null
+++ b/layouts/partials/head.html
@@ -0,0 +1,237 @@
+
+{{ $config := cond (eq $.Site.Language.Lang "en") "config" (printf "config.%s" $.Site.Language.Lang) }}
+{{ $data := index $.Site.Data $config }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ if .Title }}{{ .Title }}{{ else }}{{ $data.page_title | default $.Site.Data.config.page_title }}{{
+ end }}
+
+
+
+
+ {{ $favicon := $data.favicon | default $.Site.Data.config.favicon | default (slice (dict "rel" "shortcut icon" "type" "image/png" "href" "icon.png")) }}
+ {{ $type := (printf "%T" $favicon) }}
+ {{ if eq $type "string" }}
+ {{ $favicon | safeHTML }}
+ {{ else }}
+ {{ range $favicon }}
+
+ {{- end }}
+ {{ end }}
+
+
+ {{$sass := resources.Match "styles/[!_]*.scss" }}
+ {{$css := slice }}
+ {{range $sass}}
+ {{$scss := . | resources.ToCSS (dict "outputStyle" "compressed") }}
+ {{$css = $css | append $scss}}
+ {{end}}
+ {{if $data.enableCallouts | default $.Site.Data.config.enableCallouts}}
+ {{$scss := resources.Get "styles/_callouts.scss" | resources.ToCSS (dict "outputStyle" "compressed") }}
+ {{$css = $css | append $scss}}
+ {{end}}
+ {{$finalCss := $css | resources.Concat "styles.css" | 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 }}
+
+
+
+
+
+
+
+ {{$lightSyntax := resources.Get "styles/_light_syntax.scss" | resources.ToCSS (dict "outputStyle" "compressed") | resources.Fingerprint "md5" | resources.Minify }}
+
+
+
+ {{$scripts := (slice "js/darkmode.js" "js/util.js")}}
+ {{range $scripts}}
+ {{$scriptname := .}}
+ {{ $s := resources.Get $scriptname | resources.ExecuteAsTemplate $scriptname . | resources.Fingerprint "md5" | resources.Minify }}
+
+ {{end}}
+ {{partial "katex.html" .}}
+
+
+
+ {{ $popover := resources.Get "js/popover.js" | resources.Fingerprint "md5" |
+ resources.Minify }}
+
+
+
+ {{ if $data.enableCodeBlockTitle | default $.Site.Data.config.enableCallouts }}
+ {{ $codeTitle := resources.Get "js/code-title.js" | resources.Fingerprint "md5" | resources.Minify }}
+
+ {{end}}
+
+ {{ if $data.enableCodeBlockCopy | default $.Site.Data.config.enableCodeBlockCopy }}
+ {{ $clipboard := resources.Get "js/clipboard.js" | resources.Fingerprint "md5" | resources.Minify }}
+
+ {{ end }}
+
+ {{ if $data.enableCallouts | default $.Site.Data.config.enableCallouts }}
+ {{ $callouts := resources.Get "js/callouts.js" | resources.Fingerprint "md5" | resources.Minify }}
+
+ {{ end }}
+
+
+ {{$linkIndex := resources.Get "indices/linkIndex.json" | resources.Fingerprint
+ "md5" | resources.Minify | }} {{$contentIndex := resources.Get
+ "indices/contentIndex.json" | resources.Fingerprint "md5" | resources.Minify
+ }}
+
+ {{if $data.enableSPA | default $.Site.Data.config.enableSPA}}
+ {{ $router := resources.Get "js/router.js" | resources.Fingerprint "md5" |
+ resources.Minify }}
+
+ {{else}}
+
+ {{end}}
+
diff --git a/layouts/partials/header.html b/layouts/partials/header.html
new file mode 100644
index 000000000..a7f6733b2
--- /dev/null
+++ b/layouts/partials/header.html
@@ -0,0 +1,11 @@
+
+ {{ $config := cond (eq $.Site.Language.Lang "en") "config" (printf "config.%s" $.Site.Language.Lang) }}
+
+
+
+
{{ i18n "search" }}
+
+
+ {{partial "darkmode.html" .}}
+
+
diff --git a/layouts/partials/katex.html b/layouts/partials/katex.html
new file mode 100644
index 000000000..df53045bc
--- /dev/null
+++ b/layouts/partials/katex.html
@@ -0,0 +1,6 @@
+{{if $.Site.Data.config.enableLatex}}
+
+
+
+
+{{end}}
diff --git a/layouts/partials/page-list.html b/layouts/partials/page-list.html
new file mode 100644
index 000000000..1d2a47710
--- /dev/null
+++ b/layouts/partials/page-list.html
@@ -0,0 +1,20 @@
+
+ {{- range . -}}
+ -
+
+
+ {{partial "date-fmt.html" .}}
+
+
+
+
+
+
+ {{- end -}}
+
diff --git a/layouts/partials/recent.html b/layouts/partials/recent.html
new file mode 100644
index 000000000..e7afddd6f
--- /dev/null
+++ b/layouts/partials/recent.html
@@ -0,0 +1,12 @@
+
+
{{ i18n "recent_notes" }}
+
+ {{$notes := .Site.RegularPages}}
+ {{partial "page-list.html" (first 3 $notes)}}
+
+
diff --git a/layouts/partials/search.html b/layouts/partials/search.html
new file mode 100644
index 000000000..c169289fe
--- /dev/null
+++ b/layouts/partials/search.html
@@ -0,0 +1,18 @@
+
+{{if $.Site.Data.config.enableSemanticSearch}}
+{{ $js := resources.Get "js/semantic-search.js" | resources.ExecuteAsTemplate "js/semantic-search.js" . | resources.Fingerprint "md5" | resources.Minify }}
+
+{{else}}
+
+{{ $js := resources.Get "js/full-text-search.js" | resources.Fingerprint "md5" | resources.Minify }}
+
+{{end}}
+
diff --git a/layouts/partials/textprocessing.html b/layouts/partials/textprocessing.html
new file mode 100644
index 000000000..6776fa5b1
--- /dev/null
+++ b/layouts/partials/textprocessing.html
@@ -0,0 +1,99 @@
+{{ $content := .Content }}
+{{ $raw := .RawContent }}
+{{ $page := .Page }}
+
+{{/* Escape slashes for Latex to fix line breaks */}}
+{{$latex := findRE "(?:\\${2}([^\\$]+)\\${2})|(?:\\$([^\\$]*)\\$)" $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}}
+ {{$width := index $split 1}}
+ {{$img := printf "
" $path (default "auto" $width)}}
+ {{$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)) | urlize | lower}}
+ {{$href := strings.TrimRight "/" ($page.GetPage $title).RelPermalink}}
+
+ {{$display := default $title (index $split 1)}}
+
+ {{$display := index (last 1 (split $display "/")) 0}}
+ {{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 jumpable anchors */}}
+{{ $content = $content | replaceRE "()(.+)()" `${1}# ${3}${4}` }}
+
+{{/* Callouts */}}
+{{if $.Site.Data.config.enableCallouts}}
+ {{ $content = $content | replaceRE "" "" }}
+ {{ $blockquoteclasses := findRE `\[!.+\]` $content }}
+ {{ $blockquoteclasses1 := findRE "(.|\n)*?
" $content }}
+ {{ $blockquotetags := findRE `blockquote class=callout` $content }}
+ {{ $counter := 0 }}
+ {{ $counter1 := 0 }}
+ {{ $finder := index $blockquoteclasses1 $counter }}
+ {{range $blockquotetags}}
+ {{ $finder = index $blockquoteclasses1 $counter }}
+ {{ if (in $finder "[!") }}
+ {{ $inner := index $blockquoteclasses $counter1 }}
+ {{ if (in $finder "]-") }}
+ {{ $inner = $inner | replaceRE `\[!([a-zA-Z]+)\]` `callout-collapsible callout-collapsed ${1}`}}
+ {{ else if (in $finder "]+") }}
+ {{ $inner = $inner | replaceRE `\[!([a-zA-Z]+)\]` `callout-collapsible ${1}`}}
+ {{ else}}
+ {{ $inner = $inner | replaceRE `\[!([a-zA-Z]+)\]` `${1}` }}
+ {{ end }}
+ {{ $inner = printf "blockquote class=\"%s-callout\"" $inner | lower}}
+ {{ $content = replace $content . $inner 1}}
+ {{ $counter1 = add $counter1 1 }}
+ {{ else }}
+ {{ $inner := print "blockquote" }}
+ {{ $content = replace $content . $inner 1}}
+ {{ end }}
+ {{ $counter = add $counter 1 }}
+ {{end}}
+ {{ $content = $content | replaceRE `\[![a-zA-Z]+\][-\+]?` "" }}
+ {{ $content = $content | replaceRE "blockquote class=callout" "blockquote" }}
+{{end}}
+
+{{ $content | safeHTML }}
diff --git a/layouts/partials/toc.html b/layouts/partials/toc.html
new file mode 100644
index 000000000..4de11d3e7
--- /dev/null
+++ b/layouts/partials/toc.html
@@ -0,0 +1,16 @@
+<<<<<<< HEAD
+{{ if (and $.Site.Data.config.enableToc (ne .Params.enableToc false) (gt .WordCount 150)) }}
+