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](./screenshot.png)*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" . }} + + +
+
+

404.

+

{{ i18n "404_message" }}

+ {{ i18n "404_back" }} +
+
+ + 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 -}} +{{ .Text }} +{{- else -}} +{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}} +{{ .Text }} +{{- end -}} diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html 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" .}} +
+ +
+

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

+ Search IconIcon to open search +
+
+
+

{{ 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" .}} + + +
+ +
+

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

+ Search IconIcon to open search +
+
+
+ {{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" .}} +
+ +
+

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

+ Search IconIcon to open search +
+
+
+

{{ i18n "all_posts" . }}

+ {{with .Params.description}} +

{{.}}

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

{{ .Page.Title | humanize }}

+

{{ .Count }} {{ i18n "notes_count" }} {{if gt .Count 10}}({{ i18n "first_10"}}){{end}}

+
+ {{ with ($.Site.GetPage (printf "/tags/%s" .Page.Title)) }} + {{partial "page-list.html" (first 10 .Pages.ByLastmod.Reverse)}} + {{ end }} + {{ 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" .}} +
+ +
+

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

+ Search IconIcon to open search +
+
+
+

{{ 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}}

+ Search IconIcon to open search +
+
+
+ {{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" }}

+ 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 }} + +
+
+ {{ $name := $data.name | default $.Site.Data.config.name }} + {{ $year := dateFormat "2006" now }} +

{{ i18n "copyright" (dict "name" $name "year" $year) | safeHTML}}

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

{{ ( index $.Site.Data $config ).page_title | default $.Site.Data.config.page_title }}

+
+
+

{{ i18n "search" }}

+ {{ i18n "search_icon" }}{{ i18n "icon_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 @@ + 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)) }} +