diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml
index ee51d3e..8334ea2 100644
--- a/.github/workflows/deploy.yaml
+++ b/.github/workflows/deploy.yaml
@@ -14,7 +14,7 @@ jobs:
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.13
with:
index: true
input: content
diff --git a/assets/js/popover.js b/assets/js/popover.js
index ee0477e..ea01156 100644
--- a/assets/js/popover.js
+++ b/assets/js/popover.js
@@ -5,19 +5,20 @@ function htmlToElement(html) {
return template.content.firstChild
}
-function initPopover(baseURL) {
+function initPopover(baseURL, useContextualBacklinks) {
const basePath = baseURL.replace(window.location.origin, "")
document.addEventListener("DOMContentLoaded", () => {
fetchData.then(({ content }) => {
const links = [...document.getElementsByClassName("internal-link")]
links
- .filter(li => li.dataset.src)
+ .filter(li => li.dataset.src || (li.dataset.idx && useContextualBacklinks))
.forEach(li => {
- const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")]
- if (linkDest) {
+ if (li.dataset.ctx) {
+ console.log(li.dataset.ctx)
+ const linkDest = content[li.dataset.src]
const popoverElement = `
${linkDest.title}
-
${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...
+
${highlight(removeMarkdown(linkDest.content), li.dataset.ctx)}...
${new Date(linkDest.lastmodified).toLocaleDateString()}
`
const el = htmlToElement(popoverElement)
@@ -28,6 +29,23 @@ function initPopover(baseURL) {
li.addEventListener("mouseout", () => {
el.classList.remove("visible")
})
+ } else {
+ const linkDest = content[li.dataset.src.replace(/\/$/g, "").replace(basePath, "")]
+ if (linkDest) {
+ const popoverElement = `
+
${linkDest.title}
+
${removeMarkdown(linkDest.content).split(" ", 20).join(" ")}...
+
${new Date(linkDest.lastmodified).toLocaleDateString()}
+
`
+ 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
index f124d58..c5e293c 100644
--- a/assets/js/search.js
+++ b/assets/js/search.js
@@ -52,9 +52,65 @@ const removeMarkdown = (
return markdown
}
return output
-};
+}
// -----
+const highlight = (content, term) => {
+ const highlightWindow = 20
+
+ // try to find direct match first
+ const directMatchIdx = content.indexOf(term)
+ if (directMatchIdx !== -1) {
+ const h = highlightWindow / 2
+ const before = content.substring(0, directMatchIdx).split(" ").slice(-h)
+ const after = content.substring(directMatchIdx + term.length, content.length - 1).split(" ").slice(0, h)
+ return (before.length == h ? `...${before.join(" ")}` : before.join(" ")) + `${term}` + after.join(" ")
+ }
+
+ const tokenizedTerm = term.split(/\s+/).filter((t) => t !== '')
+ const splitText = content.split(/\s+/).filter((t) => t !== '')
+ const includesCheck = (token) =>
+ tokenizedTerm.some((term) =>
+ token.toLowerCase().startsWith(term.toLowerCase())
+ )
+
+ const occurrencesIndices = splitText.map(includesCheck)
+
+ // calculate best index
+ let bestSum = 0
+ let bestIndex = 0
+ for (
+ let i = 0;
+ i < Math.max(occurrencesIndices.length - highlightWindow, 0);
+ i++
+ ) {
+ const window = occurrencesIndices.slice(i, i + highlightWindow)
+ const windowSum = window.reduce((total, cur) => total + cur, 0)
+ if (windowSum >= bestSum) {
+ bestSum = windowSum
+ bestIndex = i
+ }
+ }
+
+ const startIndex = Math.max(bestIndex - highlightWindow, 0)
+ const endIndex = Math.min(
+ startIndex + 2 * highlightWindow,
+ splitText.length
+ )
+ const mappedText = splitText
+ .slice(startIndex, endIndex)
+ .map((token) => {
+ if (includesCheck(token)) {
+ return `${token}`
+ }
+ return token
+ })
+ .join(' ')
+ .replaceAll(' ', ' ')
+ return `${startIndex === 0 ? '' : '...'}${mappedText}${endIndex === splitText.length ? '' : '...'
+ }`
+};
+
(async function() {
const encoder = (str) => str.toLowerCase().split(/([^a-z]|[^\x00-\x7F])+/)
const contentIndex = new FlexSearch.Document({
@@ -84,52 +140,6 @@ const removeMarkdown = (
})
}
- 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)
diff --git a/assets/styles/base.scss b/assets/styles/base.scss
index 9bbd933..0787470 100644
--- a/assets/styles/base.scss
+++ b/assets/styles/base.scss
@@ -478,17 +478,17 @@ header {
& > h3, & > p {
margin: 0;
}
-
- & .search-highlight {
- background-color: #afbfc966;
- padding: 0.05em 0.2em;
- border-radius: 3px;
- }
}
}
}
}
+.search-highlight {
+ background-color: #afbfc966;
+ padding: 0.05em 0.2em;
+ border-radius: 3px;
+}
+
.section-ul {
list-style: none;
padding-left: 0;
diff --git a/data/config.yaml b/data/config.yaml
index afa531c..ccf38eb 100644
--- a/data/config.yaml
+++ b/data/config.yaml
@@ -4,6 +4,7 @@ openToc: false
enableLinkPreview: true
enableLatex: true
enableSPA: false
+enableContextualBacklinks: true
description:
Host your second brain and digital garden for free. Quartz features extremely fast full-text search,
Wikilink support, backlinks, local graph, tags, and link previews.
diff --git a/layouts/partials/backlinks.html b/layouts/partials/backlinks.html
index e42351a..23c9091 100644
--- a/layouts/partials/backlinks.html
+++ b/layouts/partials/backlinks.html
@@ -7,13 +7,18 @@
{{$inbound := index $linkIndex.index.backlinks $curPage}}
{{$contentTable := getJSON "/assets/indices/contentIndex.json"}}
{{if $inbound}}
- {{$cleanedInbound := apply (apply $inbound "index" "." "source") "replace" "." " " "-"}}
- {{- range $cleanedInbound | uniq -}}
- {{$l := printf "%s%s/" $host .}}
+ {{$backlinks := dict "SENTINEL" "SENTINEL"}}
+ {{range $k, $v := $inbound}}
+ {{$cleanedInbound := replace $v.source " " "-"}}
+ {{$ctx := $v.text}}
+ {{$backlinks = merge $backlinks (dict $cleanedInbound $ctx)}}
+ {{end}}
+ {{- range $lnk, $ctx := $backlinks -}}
+ {{$l := printf "%s%s/" $host $lnk}}
{{$l = cond (eq $l "//") "/" $l}}
- {{with (index $contentTable .)}}
+ {{with (index $contentTable $lnk)}}
- {{index (index . "title")}}
+ {{index (index . "title")}}
{{end}}
{{- end -}}
diff --git a/layouts/partials/popover.html b/layouts/partials/popover.html
index 1d16622..ba1fd03 100644
--- a/layouts/partials/popover.html
+++ b/layouts/partials/popover.html
@@ -2,6 +2,7 @@
{{ $js := resources.Get "js/popover.js" | resources.Fingerprint "md5" | resources.Minify }}
-{{end}}
\ No newline at end of file
+{{end}}