diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..40b2d4a3a
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: [jackyzha0]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..189648d9c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,32 @@
+---
+name: Bug report
+about: Something about Quartz isn't working the way you expect
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..2c9c226d9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea or improvement for Quartz
+title: ''
+labels: enhancement
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index c19bec8fd..ad9d3806a 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -12,11 +12,12 @@ jobs:
- uses: actions/checkout@v2
- name: Build Link Index
- uses: jackyzha0/hugo-obsidian@v2.5
+ uses: jackyzha0/hugo-obsidian@v2.10
with:
index: true
input: content
- output: data
+ output: assets/indices
+ root: .
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
@@ -33,4 +34,4 @@ jobs:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
publish_branch: master # deploying branch
- cname: quartz.jzhao.xyz
\ No newline at end of file
+ cname: quartz.jzhao.xyz
diff --git a/.gitignore b/.gitignore
index 1eaad6086..a7ccdb590 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,5 +3,5 @@ public
resources
.idea
content/.obsidian
-data/linkIndex.yaml
-data/contentIndex.yaml
\ No newline at end of file
+assets/indices/linkIndex.json
+assets/indices/contentIndex.json
diff --git a/.hugo_build.lock b/.hugo_build.lock
new file mode 100644
index 000000000..e69de29bb
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..c624fd59e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,93 @@
+# Citizen Code of Conduct
+
+## 1. Purpose
+
+A primary goal of the Quartz community is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof).
+
+This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior.
+
+We invite all those who participate in the Quartz community to help us create safe and positive experiences for everyone.
+
+## 2. Open [Source/Culture/Tech] Citizenship
+
+A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community.
+
+Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society.
+
+If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know.
+
+## 3. Expected Behavior
+
+The following behaviors are expected and requested of all community members:
+
+ * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community.
+ * Exercise consideration and respect in your speech and actions.
+ * Attempt collaboration before conflict.
+ * Refrain from demeaning, discriminatory, or harassing behavior and speech.
+ * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential.
+ * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations.
+
+## 4. Unacceptable Behavior
+
+The following behaviors are considered harassment and are unacceptable within our community:
+
+ * Violence, threats of violence or violent language directed against another person.
+ * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language.
+ * Posting or displaying sexually explicit or violent material.
+ * Posting or threatening to post other people's personally identifying information ("doxing").
+ * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability.
+ * Inappropriate photography or recording.
+ * Inappropriate physical contact. You should have someone's consent before touching them.
+ * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances.
+ * Deliberate intimidation, stalking or following (online or in person).
+ * Advocating for, or encouraging, any of the above behavior.
+ * Sustained disruption of community events, including talks and presentations.
+
+## 5. Weapons Policy
+
+No weapons will be allowed at Quartz community events, community spaces, or in other spaces covered by the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives (including fireworks), and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. Anyone seen in possession of one of these items will be asked to leave immediately, and will only be allowed to return without the weapon. Community members are further expected to comply with all state and local laws on this matter.
+
+## 6. Consequences of Unacceptable Behavior
+
+Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated.
+
+Anyone asked to stop unacceptable behavior is expected to comply immediately.
+
+If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event).
+
+## 7. Reporting Guidelines
+
+If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. j.zhao2k19@gmail.com.
+
+
+
+Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress.
+
+## 8. Addressing Grievances
+
+If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify @jackyzha0 with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies.
+
+
+## 9. Scope
+
+We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business.
+
+This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members.
+
+## 10. Contact info
+
+j.zhao2k19@gmail.com
+
+## 11. License and attribution
+
+The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/).
+
+Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy).
+
+_Revision 2.3. Posted 6 March 2017._
+
+_Revision 2.2. Posted 4 February 2016._
+
+_Revision 2.1. Posted 23 June 2014._
+
+_Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._
diff --git a/Makefile b/Makefile
new file mode 100644
index 000000000..038d69e94
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+.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}'
+
+serve: ## serve
+ hugo-obsidian -input=content -output=assets/indices -index -root=. && hugo server
diff --git a/assets/indices/.gitkeep b/assets/indices/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/assets/darkmode.js b/assets/js/darkmode.js
similarity index 100%
rename from assets/darkmode.js
rename to assets/js/darkmode.js
diff --git a/assets/js/graph.js b/assets/js/graph.js
new file mode 100644
index 000000000..6a6302441
--- /dev/null
+++ b/assets/js/graph.js
@@ -0,0 +1,220 @@
+async function drawGraph(url, baseUrl, pathColors, depth, enableDrag, enableLegend, enableZoom) {
+ const { index, links, content } = await fetchData
+ const curPage = url.replace(baseUrl, "")
+
+ const parseIdsFromLinks = (links) => [...(new Set(links.flatMap(link => ([link.source, link.target]))))]
+
+ const neighbours = new Set()
+ const wl = [curPage || "/", "__SENTINEL"]
+ if (depth >= 0) {
+ while (depth >= 0 && wl.length > 0) {
+ // compute neighbours
+ const cur = wl.shift()
+ if (cur === "__SENTINEL") {
+ depth--
+ wl.push("__SENTINEL")
+ } else {
+ neighbours.add(cur)
+ const outgoing = index.links[cur] || []
+ const incoming = index.backlinks[cur] || []
+ wl.push(...outgoing.map(l => l.target), ...incoming.map(l => l.source))
+ }
+ }
+ } else {
+ parseIdsFromLinks(links).forEach(id => neighbours.add(id))
+ }
+
+ const data = {
+ nodes: [...neighbours].map(id => ({ id })),
+ links: links.filter(l => neighbours.has(l.source) && neighbours.has(l.target)),
+ }
+
+ const color = (d) => {
+ if (d.id === curPage || (d.id === "/" && curPage === "")) {
+ return "var(--g-node-active)"
+ }
+
+ for (const pathColor of pathColors) {
+ const path = Object.keys(pathColor)[0]
+ const colour = pathColor[path]
+ if (d.id.startsWith(path)) {
+ return colour
+ }
+ }
+
+ return "var(--g-node)"
+ }
+
+ const drag = simulation => {
+ function dragstarted(event, d) {
+ if (!event.active) simulation.alphaTarget(1).restart();
+ d.fx = d.x;
+ d.fy = d.y;
+ }
+
+ function dragged(event, d) {
+ d.fx = event.x;
+ d.fy = event.y;
+ }
+
+ function dragended(event, d) {
+ if (!event.active) simulation.alphaTarget(0);
+ d.fx = null;
+ d.fy = null;
+ }
+
+ const noop = () => { }
+ return d3.drag()
+ .on("start", enableDrag ? dragstarted : noop)
+ .on("drag", enableDrag ? dragged : noop)
+ .on("end", enableDrag ? dragended : noop);
+ }
+
+ const height = 250
+ const width = document.getElementById("graph-container").offsetWidth
+
+ const simulation = d3.forceSimulation(data.nodes)
+ .force("charge", d3.forceManyBody().strength(-30))
+ .force("link", d3.forceLink(data.links).id(d => d.id))
+ .force("center", d3.forceCenter());
+
+ const svg = d3.select('#graph-container')
+ .append('svg')
+ .attr('width', width)
+ .attr('height', height)
+ .attr("viewBox", [-width / 2, -height / 2, width, height]);
+
+ if (enableLegend) {
+ const legend = [
+ { "Current": "var(--g-node-active)" },
+ { "Note": "var(--g-node)" },
+ ...pathColors
+ ]
+ legend.forEach((legendEntry, i) => {
+ const key = Object.keys(legendEntry)[0]
+ const colour = legendEntry[key]
+ svg.append("circle").attr("cx", -width / 2 + 20).attr("cy", height / 2 - 30 * (i + 1)).attr("r", 6).style("fill", colour)
+ svg.append("text").attr("x", -width / 2 + 40).attr("y", height / 2 - 30 * (i + 1)).text(key).style("font-size", "15px").attr("alignment-baseline", "middle")
+ })
+ }
+
+ // draw links between nodes
+ const link = svg.append("g")
+ .selectAll("line")
+ .data(data.links)
+ .join("line")
+ .attr("class", "link")
+ .attr("stroke", "var(--g-link)")
+ .attr("stroke-width", 2)
+ .attr("data-source", d => d.source.id)
+ .attr("data-target", d => d.target.id)
+
+ // svg groups
+ const graphNode = svg.append("g")
+ .selectAll("g")
+ .data(data.nodes)
+ .enter().append("g")
+
+ // draw individual nodes
+ const node = graphNode.append("circle")
+ .attr("class", "node")
+ .attr("id", (d) => d.id)
+ .attr("r", (d) => {
+ const numOut = index.links[d.id]?.length || 0
+ const numIn = index.backlinks[d.id]?.length || 0
+ return 3 + (numOut + numIn) / 4
+ })
+ .attr("fill", color)
+ .style("cursor", "pointer")
+ .on("click", (_, d) => {
+ window.location.href = baseUrl + '/' + decodeURI(d.id).replace(/\s+/g, '-')
+ })
+ .on("mouseover", function(_, d) {
+ d3.selectAll(".node")
+ .transition()
+ .duration(100)
+ .attr("fill", "var(--g-node-inactive)")
+
+ const neighbours = parseIdsFromLinks([...(index.links[d.id] || []), ...(index.backlinks[d.id] || [])])
+ const neighbourNodes = d3.selectAll(".node").filter(d => neighbours.includes(d.id))
+ const currentId = d.id
+ const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId)
+
+ // highlight neighbour nodes
+ neighbourNodes
+ .transition()
+ .duration(200)
+ .attr("fill", color)
+
+ // highlight links
+ linkNodes
+ .transition()
+ .duration(200)
+ .attr("stroke", "var(--g-link-active)")
+
+ // show text for self
+ d3.select(this.parentNode)
+ .select("text")
+ .raise()
+ .transition()
+ .duration(200)
+ .style("opacity", 1)
+ }).on("mouseleave", function(_, d) {
+ d3.selectAll(".node")
+ .transition()
+ .duration(200)
+ .attr("fill", color)
+
+ const currentId = d.id
+ const linkNodes = d3.selectAll(".link").filter(d => d.source.id === currentId || d.target.id === currentId)
+
+ linkNodes
+ .transition()
+ .duration(200)
+ .attr("stroke", "var(--g-link)")
+
+ d3.select(this.parentNode)
+ .select("text")
+ .transition()
+ .duration(200)
+ .style("opacity", 0)
+ })
+ .call(drag(simulation));
+
+ // draw labels
+ const labels = graphNode.append("text")
+ .attr("dx", 12)
+ .attr("dy", ".35em")
+ .text((d) => content[decodeURI(d.id).replace(/\s+/g, '-')]?.title || d.id.replace("-", " "))
+ .style("opacity", 0)
+ .style("pointer-events", "none")
+ .call(drag(simulation));
+
+ // set panning
+
+ if (enableZoom) {
+ svg.call(d3.zoom()
+ .extent([[0, 0], [width, height]])
+ .scaleExtent([0.25, 4])
+ .on("zoom", ({ transform }) => {
+ link.attr("transform", transform);
+ node.attr("transform", transform);
+ labels.attr("transform", transform);
+ }));
+ }
+
+ // progress the simulation
+ simulation.on("tick", () => {
+ link
+ .attr("x1", d => d.source.x)
+ .attr("y1", d => d.source.y)
+ .attr("x2", d => d.target.x)
+ .attr("y2", d => d.target.y)
+ node
+ .attr("cx", d => d.x)
+ .attr("cy", d => d.y)
+ labels
+ .attr("x", d => d.x)
+ .attr("y", d => d.y)
+ });
+}
diff --git a/assets/js/popover.js b/assets/js/popover.js
new file mode 100644
index 000000000..cf6f84b77
--- /dev/null
+++ b/assets/js/popover.js
@@ -0,0 +1,34 @@
+function htmlToElement(html) {
+ const template = document.createElement('template')
+ html = html.trim()
+ template.innerHTML = html
+ return template.content.firstChild
+}
+
+function initPopover(baseURL) {
+ const basePath = baseURL.replace(window.location.origin, "")
+ document.addEventListener("DOMContentLoaded", () => {
+ fetchData.then(({ content }) => {
+ const links = [...document.getElementsByClassName("internal-link")]
+ links.forEach(li => {
+ const linkDest = content[li.dataset.src.replace(basePath, "")]
+ // const linkDest = content[li.dataset.src]
+ if (linkDest) {
+ const popoverElement = `
`
+ const el = htmlToElement(popoverElement)
+ li.appendChild(el)
+ li.addEventListener("mouseover", () => {
+ el.classList.add("visible")
+ })
+ li.addEventListener("mouseout", () => {
+ el.classList.remove("visible")
+ })
+ }
+ })
+ })
+ })
+}
diff --git a/assets/js/search.js b/assets/js/search.js
new file mode 100644
index 000000000..34b7b621f
--- /dev/null
+++ b/assets/js/search.js
@@ -0,0 +1,247 @@
+// code from https://github.com/danestves/markdown-to-text
+const removeMarkdown = (
+ markdown,
+ options = {
+ listUnicodeChar: false,
+ stripListLeaders: true,
+ gfm: true,
+ useImgAltText: false,
+ preserveLinks: false,
+ }
+) => {
+ let output = markdown || "";
+ output = output.replace(/^(-\s*?|\*\s*?|_\s*?){3,}\s*$/gm, "");
+
+ try {
+ if (options.stripListLeaders) {
+ if (options.listUnicodeChar)
+ output = output.replace(
+ /^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm,
+ options.listUnicodeChar + " $1"
+ );
+ else output = output.replace(/^([\s\t]*)([\*\-\+]|\d+\.)\s+/gm, "$1");
+ }
+ if (options.gfm) {
+ output = output
+ .replace(/\n={2,}/g, "\n")
+ .replace(/~{3}.*\n/g, "")
+ .replace(/~~/g, "")
+ .replace(/`{3}.*\n/g, "");
+ }
+ if (options.preserveLinks) {
+ output = output.replace(/\[(.*?)\][\[\(](.*?)[\]\)]/g, "$1 ($2)")
+ }
+ output = output
+ .replace(/<[^>]*>/g, "")
+ .replace(/^[=\-]{2,}\s*$/g, "")
+ .replace(/\[\^.+?\](\: .*?$)?/g, "")
+ .replace(/\s{0,2}\[.*?\]: .*?$/g, "")
+ .replace(/\!\[(.*?)\][\[\(].*?[\]\)]/g, options.useImgAltText ? "$1" : "")
+ .replace(/\[(.*?)\][\[\(].*?[\]\)]/g, "$1")
+ .replace(/^\s{0,3}>\s?/g, "")
+ .replace(/(^|\n)\s{0,3}>\s?/g, "\n\n")
+ .replace(/^\s{1,2}\[(.*?)\]: (\S+)( ".*?")?\s*$/g, "")
+ .replace(
+ /^(\n)?\s{0,}#{1,6}\s+| {0,}(\n)?\s{0,}#{0,} {0,}(\n)?\s{0,}$/gm,
+ "$1$2$3"
+ )
+ .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, "$2")
+ .replace(/([\*_]{1,3})(\S.*?\S{0,1})\1/g, "$2")
+ .replace(/(`{3,})(.*?)\1/gm, "$2")
+ .replace(/`(.+?)`/g, "$1")
+ .replace(/\n{2,}/g, "\n\n");
+ } catch (e) {
+ console.error(e);
+ return markdown;
+ }
+ return output;
+};
+// -----
+
+(async function () {
+ const contentIndex = new FlexSearch.Document({
+ cache: true,
+ charset: "latin:extra",
+ optimize: true,
+ worker: true,
+ document: {
+ index: [{
+ field: "content",
+ tokenize: "strict",
+ context: {
+ resolution: 5,
+ depth: 3,
+ bidirectional: true
+ },
+ suggest: true,
+ }, {
+ field: "title",
+ tokenize: "forward",
+ }]
+ }
+ })
+
+ const { content } = await fetchData
+ for (const [key, value] of Object.entries(content)) {
+ contentIndex.add({
+ id: key,
+ title: value.title,
+ content: removeMarkdown(value.content),
+ })
+ }
+
+ const highlight = (content, term) => {
+ const highlightWindow = 20
+ const tokenizedTerm = term.split(/\s+/).filter(t => t !== "")
+ const splitText = content.split(/\s+/).filter(t => t !== "")
+ const includesCheck = (token) => tokenizedTerm.some(term => token.toLowerCase().startsWith(term.toLowerCase()))
+
+ const occurrencesIndices = splitText
+ .map(includesCheck)
+
+ // calculate best index
+ let bestSum = 0
+ let bestIndex = 0
+ for (let i = 0; i < Math.max(occurrencesIndices.length - highlightWindow, 0); i++) {
+ const window = occurrencesIndices.slice(i, i + highlightWindow)
+ const windowSum = window.reduce((total, cur) => total + cur, 0)
+ if (windowSum >= bestSum) {
+ bestSum = windowSum
+ bestIndex = i
+ }
+ }
+
+ const startIndex = Math.max(bestIndex - highlightWindow, 0)
+ const endIndex = Math.min(startIndex + 2 * highlightWindow, splitText.length)
+ const mappedText = splitText
+ .slice(startIndex, endIndex)
+ .map(token => {
+ if (includesCheck(token)) {
+ return `${token}`
+ }
+ return token
+ })
+ .join(" ")
+ .replaceAll(' ', " ")
+ return `${startIndex === 0 ? "" : "..."}${mappedText}${endIndex === splitText.length ? "" : "..."}`
+ }
+
+ const resultToHTML = ({ url, title, content, term }) => {
+ const text = removeMarkdown(content)
+ const resultTitle = highlight(title, term)
+ const resultText = highlight(text, term)
+ return ``
+ }
+
+ const redir = (id, term) => {
+ window.location.href = BASE_URL + `${id}#:~:text=${encodeURIComponent(term)}`
+ }
+
+ const formatForDisplay = id => ({
+ id,
+ url: id,
+ title: content[id].title,
+ content: content[id].content
+ })
+
+ const source = document.getElementById('search-bar')
+ const results = document.getElementById("results-container")
+ let term
+ source.addEventListener("keyup", (e) => {
+ if (e.key === "Enter") {
+ const anchor = document.getElementsByClassName("result-card")[0]
+ redir(anchor.id, term)
+ }
+ })
+ source.addEventListener('input', (e) => {
+ term = e.target.value
+ contentIndex.search(term, [
+ {
+ field: "content",
+ limit: 10,
+ suggest: true,
+ },
+ {
+ field: "title",
+ limit: 5,
+ }
+ ]).then(searchResults => {
+ const getByField = field => {
+ const results = searchResults.filter(x => x.field === field)
+ if (results.length === 0) {
+ return []
+ } else {
+ return [...results[0].result]
+ }
+ }
+ const allIds = new Set([...getByField('title'), ...getByField('content')])
+ const finalResults = [...allIds].map(formatForDisplay)
+
+ // display
+ if (finalResults.length === 0) {
+ results.innerHTML = ``
+ } else {
+ results.innerHTML = finalResults
+ .map(result => resultToHTML({
+ ...result,
+ term,
+ }))
+ .join("\n")
+ const anchors = document.getElementsByClassName("result-card");
+ [...anchors].forEach(anchor => {
+ anchor.onclick = () => redir(anchor.id, term)
+ })
+ }
+ })
+ })
+
+
+ const searchContainer = document.getElementById("search-container")
+
+ function openSearch() {
+ if (searchContainer.style.display === "none" || searchContainer.style.display === "") {
+ source.value = ""
+ results.innerHTML = ""
+ searchContainer.style.display = "block"
+ source.focus()
+ } else {
+ searchContainer.style.display = "none"
+ }
+ }
+
+ function closeSearch() {
+ searchContainer.style.display = "none"
+ }
+
+ document.addEventListener('keydown', (event) => {
+ if (event.key === "/") {
+ event.preventDefault()
+ openSearch()
+ }
+ if (event.key === "Escape") {
+ event.preventDefault()
+ closeSearch()
+ }
+ })
+
+ const searchButton = document.getElementById("search-icon")
+ searchButton.addEventListener('click', (evt) => {
+ openSearch()
+ })
+ searchButton.addEventListener('keydown', (evt) => {
+ openSearch()
+ })
+ searchContainer.addEventListener('click', (evt) => {
+ closeSearch()
+ })
+ document.getElementById("search-space").addEventListener('click', (evt) => {
+ evt.stopPropagation()
+ })
+})()
+
diff --git a/assets/base.scss b/assets/styles/base.scss
similarity index 67%
rename from assets/base.scss
rename to assets/styles/base.scss
index bd0b6091e..d5b515007 100644
--- a/assets/base.scss
+++ b/assets/styles/base.scss
@@ -6,7 +6,7 @@
--lt-colours-gray: var(--outlinegray) !important;
}
-h1, h2, h3, h4, ol, ul, thead {
+h1, h2, h3, h4, h5, h6, ol, ul, thead {
font-family: Inter;
color: var(--dark);
font-weight: revert;
@@ -23,19 +23,6 @@ p, ul, text {
padding: revert;
}
-a {
- font-family: Inter;
- font-weight: 700;
- font-size: 1em;
- text-decoration: none;
- transition: all 0.2s ease;
- color: var(--secondary);
-
- &:hover {
- color: var(--tertiary) !important;
- }
-}
-
#TableOfContents > ol {
counter-reset: section;
margin-left: 0em;
@@ -94,14 +81,6 @@ p, tbody, li {
line-height: 1.5em;
}
-h2 {
- opacity: 0.85;
-}
-
-h3 {
- opacity: 0.75;
-}
-
blockquote {
margin-left: 0em;
border-left: 3px solid var(--secondary);
@@ -125,12 +104,94 @@ td, th {
margin: 0.5em 0;
}
-article a {
- font-family: Source Sans Pro;
+.pagination {
+ list-style: none;
+ padding-left: 0;
+ display: flex;
+ margin-top: 2em;
+ gap: 1.5em;
+ justify-content: center;
+
+ & > li {
+ text-align: center;
+ display: inline-block;
+
+ & a {
+ background-color: transparent !important;
+ }
+
+ & a[href$="#"] {
+ opacity: 0.2;
+ }
+ }
+}
+
+.section {
+ & h3 > a {
+ font-weight: 700;
+ font-family: Inter;
+ margin: 0;
+ }
+ & p {
+ margin-top: 0;
+ }
+}
+
+article {
+ & > .meta {
+ margin: -1.5em 0 1em 0;
+ opacity: 0.7;
+ }
+
+ & > .tags {
+ list-style: none;
+ padding-left: 0;
+
+ & .meta {
+ & > h1 {
+ margin: 0;
+ }
+ & > p {
+ margin: 0;
+ }
+ }
+
+ & > li {
+ display: inline-block;
+ }
+ & > li > a {
+ border-radius: 8px;
+ border: var(--outlinegray) 1px solid;
+ padding: 0.2em 0.5em;
+ &::before {
+ content: "#";
+ margin-right: 0.3em;
+ color: var(--outlinegray);
+ }
+ }
+ }
+
+ & a {
+ font-family: Source Sans Pro;
+ font-weight: 600;
+
+ &.internal-link {
+ text-decoration: none;
+ background-color: transparentize(#8f9fa9, 0.85);
+ padding: 0 0.1em;
+ margin: auto -0.1em;
+ border-radius: 3px;
+ }
+ }
+
+ & p {
+ overflow-wrap: anywhere;
+ }
+}
+
+.backlinks a {
font-weight: 600;
- text-decoration: underline;
- text-decoration-color: var(--tertiary);
- text-decoration-thickness: .15em;
+ font-size: 0.9rem;
}
sup > a {
@@ -138,6 +199,19 @@ sup > a {
padding: 0 0.1em 0 0.2em;
}
+a {
+ font-family: Inter, sans-serif;
+ font-size: 1em;
+ font-weight: 700;
+ text-decoration: none;
+ transition: all 0.2s ease;
+ color: var(--secondary);
+
+ &:hover {
+ color: var(--tertiary) !important;
+ }
+}
+
pre {
font-family: 'Fira Code';
padding: 0.75em;
@@ -155,13 +229,22 @@ code {
html {
scroll-behavior: smooth;
+
+ &:lang(ar) {
+ & p, & h1, & h2, & h3, article {
+ direction: rtl;
+ text-align: right;
+ }
+ }
}
body {
margin: 0;
height: 100vh;
width: 100vw;
- overflow-x: hidden;
+ //overflow-x: hidden;
+ max-width: 100%;
+ box-sizing: border-box;
background-color: var(--light);
}
@@ -190,25 +273,18 @@ hr {
background-color: var(--dark);
}
-// internal link
-a[href^="/"] {
- text-decoration: none;
- background-color: #afbfc922;
- padding: 0 0.2em;
- border-radius: 3px;
-}
-
.singlePage {
- margin: 4em 30vw;
+ padding: 4em 30vw;
@media all and (max-width: 1200px) {
- margin: 25px 5vw;
+ padding: 25px 5vw;
}
}
.page-end {
display: flex;
flex-direction: row;
+ gap: 2em;
@media all and (max-width: 780px) {
flex-direction: column;
@@ -222,7 +298,6 @@ a[href^="/"] {
& > ul {
list-style: none;
padding-left: 0;
- margin-right: 2em;
& > li {
margin: 0.5em 0;
@@ -235,7 +310,7 @@ a[href^="/"] {
& #graph-container {
border: var(--outlinegray) 1px solid;
- border-radius: 5px
+ border-radius: 5px;
}
}
@@ -243,6 +318,10 @@ a[href^="/"] {
margin-top: 30vh;
}
+article > h1 {
+ font-size: 2em;
+}
+
header {
display: flex;
flex-direction: row;
@@ -370,4 +449,70 @@ header {
}
}
}
-}
\ No newline at end of file
+}
+
+.section-ul {
+ list-style: none;
+ padding-left: 0;
+
+ & > li {
+ border: 1px solid var(--outlinegray);
+ border-radius: 5px;
+ padding: 0 1em;
+ margin-bottom: 1em;
+
+ & h3 {
+ opacity: 1;
+ font-weight: 700;
+ margin-bottom: 0em;
+ }
+
+ & .meta {
+ opacity: 0.6;
+ }
+ }
+}
+
+.popover {
+ z-index: 999;
+ position: absolute;
+ width: 20em;
+ display: inline-block;
+ visibility: hidden;
+ background-color: var(--light);
+ padding: 1em;
+ border: 1px solid var(--outlinegray);
+ border-radius: 5px;
+ transform: translate(-50%, 40%);
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.2s ease, transform 0.2s ease;
+ transition-delay: 0.3s;
+ user-select: none;
+
+ @media all and (max-width: 600px) {
+ display: none;
+ }
+
+ &.visible {
+ opacity: 1;
+ visibility: visible;
+ transform: translate(-50%, 20%);
+ }
+
+ & > h3 {
+ font-size: 1rem;
+ margin: 0.25em 0;
+ }
+
+ & > .meta {
+ margin-top: 0.25em;
+ opacity: 0.5;
+ }
+
+ & > p {
+ margin: 0;
+ font-weight: 400;
+ user-select: none;
+ }
+}
diff --git a/assets/custom.scss b/assets/styles/custom.scss
similarity index 93%
rename from assets/custom.scss
rename to assets/styles/custom.scss
index cb8babb35..612b80a61 100644
--- a/assets/custom.scss
+++ b/assets/styles/custom.scss
@@ -14,7 +14,7 @@
[saved-theme="dark"] {
--light: #1e1e21 !important;
--dark: #fbfffe !important;
- --secondary: #5b778a !important;
+ --secondary: #6b879a !important;
--visited: #4a575e !important;
--tertiary: #84a59d !important;
--primary: #f58382 !important;
diff --git a/assets/darkmode.scss b/assets/styles/darkmode.scss
similarity index 100%
rename from assets/darkmode.scss
rename to assets/styles/darkmode.scss
diff --git a/assets/syntax.scss b/assets/styles/syntax.scss
similarity index 100%
rename from assets/syntax.scss
rename to assets/styles/syntax.scss
diff --git a/config.toml b/config.toml
index dabe513d1..5c4dfcb40 100644
--- a/config.toml
+++ b/config.toml
@@ -2,12 +2,15 @@ baseURL = "https://quartz.jzhao.xyz/"
languageCode = "en-us"
googleAnalytics = "G-XYFD95KB4J"
pygmentsUseClasses = true
-relativeURLs = true
+relativeURLs = false
disablePathToLower = true
ignoreFiles = [
"/content/templates/*",
"/content/private/*",
]
+summaryLength = 20
+paginate = 10
+enableGitInfo = true
[markup]
[markup.tableOfContents]
@@ -24,4 +27,4 @@ ignoreFiles = [
lineNos = true
lineNumbersInTable = true
style = "dracula"
- tabWidth = 4
\ No newline at end of file
+ tabWidth = 4
diff --git a/content/_index.md b/content/_index.md
index d9815914d..4d5bcc31c 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -1,18 +1,27 @@
---
-title: ðŠī Quartz v2.1
+title: ðŠī Quartz 3.1
---
Host your second brain and [digital garden](https://jzhao.xyz/posts/digital-gardening) for free. Quartz features
1. Extremely fast full-text search by pressing `/`
-2. Beautiful, out-of-the-box website creation and deployment
-3. Display for backlinks of each note
-4. A customizable graph view
-5. Endlessly powerful page and theme customization
+2. Display for backlinks of each note
+3. Fully customizable local graph view
+4. Endlessly powerful page and theme customization using CSS
+5. Automatically generated tag and section lists of content
+6. Beautiful link previews
## Get Started
> ð [Setup your own digital garden using Quartz](notes/setup.md)
-Not convinced yet? Look at some [community digital gardens](moc/showcase) built with Quartz, or read about [why I made Quartz](notes/philosophy.md) to begin with!
+Not convinced yet? Look at some [community digital gardens](notes/showcase.md) built with Quartz, or read about [why I made Quartz](notes/philosophy.md) to begin with!
+
+## Content Lists
+If you prefer browsing the contents of this site through a list instead of a graph, you can find content lists here too:
+
+- [All Notes](/notes)
+- [Setup-related Notes](/tags/setup)
## Troubleshooting
- ð§ [Troubleshooting and FAQ](notes/troubleshooting.md)
-- ð [Submit an Issue](https://github.com/jackyzha0/quartz/issues)
\ No newline at end of file
+- ð [Submit an Issue](https://github.com/jackyzha0/quartz/issues)
+- ð [Discord Community](https://discord.gg/cRFFHYye7t)
+
diff --git a/content/notes/Obsidian.md b/content/notes/Obsidian.md
new file mode 100644
index 000000000..e2d2cf98b
--- /dev/null
+++ b/content/notes/Obsidian.md
@@ -0,0 +1,30 @@
+---
+title: "Obsidian Vault Integration"
+tags:
+- setup
+---
+
+## Setup
+Obsidian is the preferred way to use Quartz. You can either create a new Obsidian Vault or link one that your already have.
+
+### New Vault
+If you don't have an existing Vault, [download Obsidian](https://obsidian.md/) and create a new Vault in the `/content` folder that you created and cloned during the [setup](notes/setup.md) step.
+
+### Linking an existing Vault
+The easiest way to use an existing Vault is to copy all of your files (directory and hierarchies intact) into the `/content` folder.
+
+## Settings
+Great, now that you have your Obsidian linked to your Quartz, let's fix some settings so that they play well.
+
+1. Under Options > Files and Links, set the New link format to always be Absolute Path in Vault and disabled WikiLinks so Obsidian generates regular Markdown links.
+2. Go to Settings > Files & Links > Turn "on" automatically update internal links.
+3. Use [[Wikilinks]] should be off (Wikilinks are not officially supported yet)
+
+*Obsidian Settings*
+
+## Templates
+Inserting front matter everytime you want to create a new Note gets annoying really quickly. Luckily, Obsidian supports templates which makes inserting new content really easily.
+
+**If you decide to overwrite the `/content` folder completely, don't remove the `/content/templates` folder!**
+
+Head over to Options > Core Plugins and enable the Templates plugin. Then go to Options > Hotkeys and set a hotkey for 'Insert Template' (I recomment `[cmd]+T`). That way, when you create a new note, you can just press the hotkey for a new template and be ready to go!
\ No newline at end of file
diff --git a/content/notes/config.md b/content/notes/config.md
index 44dca4d3b..4c90b401e 100644
--- a/content/notes/config.md
+++ b/content/notes/config.md
@@ -1,5 +1,7 @@
---
title: "Configuration"
+tags:
+- setup
---
@@ -11,6 +13,7 @@ title: "Configuration"
```yaml
name: Your name here! # Shows in the footer
enableToc: true # Whether to show a Table of Contents
+enableLinkPreview: true # whether to render card previews for links
description: Page description to show to search engines
page_title: Quartz Example Page # Default Page Title
@@ -28,6 +31,7 @@ To customize the Interactive Graph view, you can poke around `data/graphConfig.y
enableLegend: false # automatically generate a legend
enableDrag: true # allow dragging nodes in the graph
enableZoom: true # allow zooming and panning the graph
+depth: -1 # how many neighbours of the current node to show (-1 is all nodes)
paths: # colour specific nodes path off of their path
- /moc: "#4388cc"
```
@@ -43,4 +47,20 @@ For example, the structure of the home page can be edited through `/layouts/inde
More info about partials on [Hugo's website.](https://gohugo.io/templates/partials/)
-Still having problems? Checkout our [FAQ and Troubleshooting guide](notes/troubleshooting.md).
\ No newline at end of file
+Still having problems? Checkout our [FAQ and Troubleshooting guide](notes/troubleshooting.md).
+
+## Multilingual
+Want to support languages that read from right-to-left (like Arabic)? Hugo (and by proxy, Quartz) supports this natively.
+
+Follow the steps [Hugo provides here](https://gohugo.io/content-management/multilingual/#configure-languages) and modify your `config.toml`
+
+For example:
+
+```toml
+defaultContentLanguage = 'ar'
+[languages]
+ [languages.ar]
+ languagedirection = 'rtl'
+ title = 'Ų ØŊŲŲØŠŲ'
+ weight = 1
+```
\ No newline at end of file
diff --git a/content/notes/custom Domain.md b/content/notes/custom Domain.md
new file mode 100644
index 000000000..72cb466eb
--- /dev/null
+++ b/content/notes/custom Domain.md
@@ -0,0 +1,17 @@
+---
+title: "Custom Domain"
+---
+
+### Registrar
+This step is only applicable if you are using a **custom domain**! If you are using a `.github.io` domain, you can skip this step.
+
+For this last bit to take effect, you also need to create a CNAME record with the DNS provider you register your domain with (i.e. NameCheap, Google Domains).
+
+GitHub has some [documentation on this](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site), but the tldr; is to
+
+1. Go to your forked repository (`github.com//quartz`) settings page and go to the Pages tab. Under "Custom domain", type your custom domain, then click **Save**.
+2. Go to your DNS Provider and create a CNAME record that points from your domain to `.github.io` (which GitHub gives to you for free!) as your domain.
+Here, we take advantage of GitHub's free page hosting to deploy our site. Change `baseURL` in `/config.toml`.
-[Reference.](https://github.com/jackyzha0/quartz/blob/hugo/config.toml)
+Make sure that your `baseURL` has a trailing `/`!
+
+[Reference `config.toml` here](https://github.com/jackyzha0/quartz/blob/hugo/config.toml)
```toml
baseURL = "https:///"
```
+If you are using this under a subdomain (e.g. `.github.io/quartz`), include the trailing `/`.
+
+```toml
+baseURL = "https://.github.io/quartz/"
+```
+
Change `cname` in `/.github/workflows/deploy.yaml`. Again, if you don't have a custom domain to use, you can use `.github.io`.
-[Reference.](https://github.com/jackyzha0/quartz/blob/hugo/.github/workflows/deploy.yaml)
+Please note that the `cname` field should *not* have any path `e.g. end with /quartz` or have a trailing `/`.
+
+[Reference `deploy.yaml` here](https://github.com/jackyzha0/quartz/blob/hugo/.github/workflows/deploy.yaml)
```yaml
- name: Deploy
@@ -59,22 +73,7 @@ Change `cname` in `/.github/workflows/deploy.yaml`. Again, if you don't have a c
cname:
```
-### Registrar
-This step is only applicable if you are using a **custom domain**! If you are using `.github.io`, you can skip this step.
-
-For this last bit to take effect, you also need to create a CNAME record with the DNS provider you register your domain with (i.e. NameCheap, Google Domains).
-
-GitHub has some [documentation on this](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site), but the tldr; is to
-
-1. Go to your forked repository (`github.com//quartz`) settings page and go to the Pages tab. Under "Custom domain", type your custom domain, then click **Save**.
-2. Go to your DNS Provider and create a CNAME record that points from your domain to ` Files and Links, set the New link format to always be Absolute Path in Vault and disabled WikiLinks so Obsidian generates regular Markdown links.
+1. Under Options > Files and Links, set the New link format to always be Absolute Path in Vault and disabled WikiLinks so Obsidian generates regular Markdown links.
+2. Go to Settings > Files & Links > Turn "on" automatically update internal links.
+3. Use [[Wikilinks]] should be off (Wikilinks are not officially supported yet)
*Obsidian Settings*
## Templates
-Inserting front matter everytime you want to create a new Note gets really annoying really quickly. Luckily, Obsidian supports templates which makes inserting new content really easily.
+Inserting front matter everytime you want to create a new Note gets annoying really quickly. Luckily, Obsidian supports templates which makes inserting new content really easily.
**If you decide to overwrite the `/content` folder completely, don't remove the `/content/templates` folder!**
-Head over to Options > Core Plugins and enable the Templates plugin. Then go to Options > Hotkeys and set a hotkey for 'Insert Template'. That way, when you create a new note, you can just press the hotkey for a new template and be ready to go!
\ No newline at end of file
+Head over to Options > Core Plugins and enable the Templates plugin. Then go to Options > Hotkeys and set a hotkey for 'Insert Template' (I recomment `[cmd]+T`). That way, when you create a new note, you can just press the hotkey for a new template and be ready to go!
\ No newline at end of file
diff --git a/content/notes/preview changes.md b/content/notes/preview changes.md
index dac619e0e..7f12f3055 100644
--- a/content/notes/preview changes.md
+++ b/content/notes/preview changes.md
@@ -4,24 +4,28 @@ title: "Preview Changes"
If you'd like to preview what your Quartz site looks like before deploying it to the internet, here's exactly how to do that!
+Note that both of these steps need to be completed.
+
## Install `hugo-obsidian`
This step will generate the list of backlinks for Hugo to parse. Ensure you have [Go](https://golang.org/doc/install) (>= 1.16) installed.
```shell
# Install and link `hugo-obsidian` locally
-$ go install github.com/jackyzha0/hugo-obsidian
+$ go install github.com/jackyzha0/hugo-obsidian@latest
# Navigate to your local Quartz folder
$ cd
# Scrape all links in your Quartz folder and generate info for Quartz
-$ hugo-obsidian -input=content -output=data -index=true
+$ hugo-obsidian -input=content -output=assets/indices -index -root=.
```
+If you are running into an error saying that `command not found: hugo-obsidian`, make sure you set your `GOPATH` correctly! This will allow your terminal to correctly recognize hugo-obsidian as an executable.
+
Afterwards, start the Hugo server as shown above and your local backlinks and interactive graph should be populated!
## Installing Hugo
-Hugo is the static site generator that powers Quartz. If you'd like to preview your site locally, [install Hugo](https://gohugo.io/getting-started/installing/).
+Hugo is the static site generator that powers Quartz. [Install Hugo with "extended" Sass/SCSS version](https://gohugo.io/getting-started/installing/) first. Then,
```
# Navigate to your local Quartz folder
@@ -31,4 +35,4 @@ $ cd
$ hugo server
# View your site in a browser at http://localhost:1313/
-```
\ No newline at end of file
+```
diff --git a/content/notes/setup.md b/content/notes/setup.md
index 2cd463d7f..ea417df4d 100644
--- a/content/notes/setup.md
+++ b/content/notes/setup.md
@@ -1,5 +1,7 @@
---
title: "Setup"
+tags:
+- setup
---
## Making your own Quartz
@@ -26,4 +28,16 @@ Great! Now you have everything you need to start editing and growing your digita
âïļ [Editing Notes in Quartz](notes/editing.md)
-Having problems? Checkout our [FAQ and Troubleshooting guide](notes/troubleshooting.md).
\ No newline at end of file
+Having problems? Checkout our [FAQ and Troubleshooting guide](notes/troubleshooting.md).
+
+## Updating
+Haven't updated Quartz in a while and want all the cool new optimizations?
+
+```shell
+# add Quartz as a remote host
+git remote add upstream git@github.com:jackyzha0/quartz.git
+
+# index and fetch changes
+git fetch upstream
+git checkout upstream/hugo -- layouts .github Makefile assets config.toml data layouts static
+```
\ No newline at end of file
diff --git a/content/moc/showcase.md b/content/notes/showcase.md
similarity index 82%
rename from content/moc/showcase.md
rename to content/notes/showcase.md
index 20e6a14de..061cba7f8 100644
--- a/content/moc/showcase.md
+++ b/content/notes/showcase.md
@@ -12,5 +12,7 @@ Want to see what Quartz can do? Here are some cool community gardens :)
- [Chloe's Garden](https://garden.chloeabrasada.online/)
- [SlRvb's Site](https://slrvb.github.io/Site/)
- [Course notes for Information Technology Advanced Theory](https://a2itnotes.github.io/quartz/)
+- [Brandon Boswell's Garden](https://brandonkboswell.com)
+- [Siyang's Courtyard](https://siyangsun.github.io/courtyard/)
-If you want to see your own on here, submit a [Pull Request adding yourself to this file](https://github.com/jackyzha0/quartz/blob/hugo/content/moc/showcase.md)!
+If you want to see your own on here, submit a [Pull Request adding yourself to this file](https://github.com/jackyzha0/quartz/blob/hugo/content/notes/showcase.md)!
diff --git a/content/notes/troubleshooting.md b/content/notes/troubleshooting.md
index a0bf56968..226216be5 100644
--- a/content/notes/troubleshooting.md
+++ b/content/notes/troubleshooting.md
@@ -2,7 +2,24 @@
title: "Troubleshooting and FAQ"
---
-## Common Pitfalls
+Still having trouble? Here are a list of common questions and problems people encounter when installing Quartz.
+
+While you're here, join our [Discord](https://discord.gg/cRFFHYye7t) :)
+
+### My GitHub pages is just showing the README and not Quartz
+Make sure you set the source to deploy from `master` (and not `hugo`) using `/ (root)`! See more in the [hosting](/notes/hosting) guide
+
+### Some of my pages have 'January 1, 0001' as the last modified date
+This is a problem caused by `git` treating files as case-insensitive by default and some of your posts probably have capitalized file names. You can turn this off in your Quartz by running this command.
+
+```shell
+# in the root of your Quartz (same folder as config.toml)
+git config core.ignorecase true
+
+# or globally (not recommended)
+git config --global core.ignorecase true
+```
+
### Can I publish only a subset of my pages?
Yes! Quartz makes selective publishing really easy. Heres a guide on [excluding pages from being published](notes/ignore%20notes.md).
@@ -25,7 +42,7 @@ source ~/.bash_profile
```
### How come my notes aren't being rendered?
-You probably forgot to include front matter in your Markdown files. You can either setup [Obsidian](notes/obsidian) to do this for you or you need to manually define it. More details in [the 'how to edit' guide](notes/editing.md).
+You probably forgot to include front matter in your Markdown files. You can either setup [obsidian](notes/obsidian.md) to do this for you or you need to manually define it. More details in [the 'how to edit' guide](notes/editing.md).
### My custom domain isn't working!
Walk through the steps in [the hosting guide](notes/hosting.md) again. Make sure you wait 30 min to 1 hour for changes to take effect.
diff --git a/data/config.yaml b/data/config.yaml
index 49479077b..a8f78bc23 100644
--- a/data/config.yaml
+++ b/data/config.yaml
@@ -1,10 +1,11 @@
name: Jacky Zhao
enableToc: true
+enableLinkPreview: true
description:
Here is the page description. This is an example Quartz site that details installation,
setup, customization, and troubleshooting for Quartz itself.
page_title:
- Quartz Example Page
+ "ðŠī Quartz 3.1"
links:
- link_name: Twitter
link: https://twitter.com/_jzhao
diff --git a/data/graphConfig.yaml b/data/graphConfig.yaml
index 4104e428a..3f8d3fb6b 100644
--- a/data/graphConfig.yaml
+++ b/data/graphConfig.yaml
@@ -1,5 +1,6 @@
enableLegend: false
enableDrag: true
enableZoom: true
+depth: -1 # set to -1 to show full graph
paths:
- /moc: "#4388cc"
\ No newline at end of file
diff --git a/layouts/_default/_markup/render-image.html b/layouts/_default/_markup/render-image.html
new file mode 100644
index 000000000..ff4e8b39c
--- /dev/null
+++ b/layouts/_default/_markup/render-image.html
@@ -0,0 +1,8 @@
+{{$src := .Destination | safeURL }}
+{{$external := strings.HasPrefix $src "http" }}
+{{- if $external -}}
+
+{{- else -}}
+{{$fixedUrl := (cond (hasPrefix $src "/") $src (print "/" $src)) | urlize}}
+
+{{- end -}}
diff --git a/layouts/_default/_markup/render-link.html b/layouts/_default/_markup/render-link.html
index f37614f07..7ae8a3e92 100644
--- a/layouts/_default/_markup/render-link.html
+++ b/layouts/_default/_markup/render-link.html
@@ -1,8 +1,11 @@
{{$trimmed := strings.TrimSuffix ".md" (.Destination | safeURL)}}
-{{$trimmed = replace $trimmed "%20" "-"}}
-{{$external := strings.HasPrefix $trimmed "http" }}
-{{ if $external }}
-{{ .Text | safeHTML }}
-{{else}}
-{{ .Text | safeHTML }}
-{{end}}
\ No newline at end of file
+{{$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}}
+{{$rooted := default $spacedurl (strings.TrimRight "/" (.Page.GetPage $spacedurl).RelPermalink) }}
+{{ .Text | safeHTML }}
+{{- 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..1a4aae058
--- /dev/null
+++ b/layouts/_default/section.html
@@ -0,0 +1,25 @@
+
+
+{{ partial "head.html" . }}
+
+
+{{partial "search.html" .}}
+
{{- end -}}
{{else}}
@@ -18,4 +18,4 @@
No backlinks found
{{end}}
-
\ No newline at end of file
+
diff --git a/layouts/partials/contact.html b/layouts/partials/contact.html
new file mode 100644
index 000000000..748cb0615
--- /dev/null
+++ b/layouts/partials/contact.html
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html
index 416b16733..6d4ef17b9 100644
--- a/layouts/partials/footer.html
+++ b/layouts/partials/footer.html
@@ -8,15 +8,4 @@
-
-
-
-
\ No newline at end of file
+{{partial "contact.html" .}}
\ No newline at end of file
diff --git a/layouts/partials/graph.html b/layouts/partials/graph.html
index dbd33d1b4..ca379689b 100644
--- a/layouts/partials/graph.html
+++ b/layouts/partials/graph.html
@@ -1,4 +1,4 @@
-
+