Skip to content

Commit

Permalink
add search fix #17
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp committed Jul 1, 2024
1 parent 903b9bd commit 15fd7bb
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 1 deletion.
17 changes: 16 additions & 1 deletion src/_includes/layout.jlhtml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,18 @@ $(let d = get(page.output.frontmatter, "description", nothing)
end
end)

<link rel="root" href="$(root_url)" />

<link rel="icon" href="$(root_url)/assets/favicon.ico" />
<link rel="icon" href="$(root_url)/assets/favicon.svg" type="image/svg+xml">

<link rel="stylesheet" href="$(root_url)/assets/styles/index.css" type="text/css" />
<link rel="stylesheet" href="$(root_url)/assets/styles/layout.css" type="text/css" />
<script src="$(root_url)/assets/scripts/sidebar.js" type="module" defer></script>
<link rel="pp-search-data" href="$(root_url)/pp_search_data.json" />
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lunr.min.js" integrity="sha256-DFDZACuFeAqEKv/7Vnu1Tt5ALa58bcWZegGGFNgET8g=" crossorigin="anonymous" defer></script>
<script src="$(root_url)/assets/scripts/search.js" type="module" defer></script>


$(pluto_head)
</head>
Expand All @@ -44,6 +49,16 @@ $(pluto_head)
<ul>
<li class="pluto_home_link"><a class="pluto_home_link" href=$(root_url)><img src="$(root_url)/assets/favicon.svg"><span> Pluto.jl</span></a>
</li>

<li>
<div class="search-bar">
<form action="$(root_url)/en/docs/search" method="GET">
<input type="search" name="q" placeholder="Search...">
<input type=submit value="🔎">
</form>
</div>
</li>

$(let
sidebar_data = Base.include(@__MODULE__, joinpath(@__DIR__, "..", "sidebar data.jl"))

Expand Down Expand Up @@ -94,4 +109,4 @@ $(pluto_head)
</div>
</div>
</body>
</html>
</html>
148 changes: 148 additions & 0 deletions src/assets/scripts/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
const root_href = document.head.querySelector("link[rel='root']").getAttribute("href")

const minby = (arr, fn) => arr.reduce((a, b) => (fn(a) < fn(b) ? a : b))
const maxby = (arr, fn) => arr.reduce((a, b) => (fn(a) > fn(b) ? a : b))
const range = (length) => [...Array(length).keys()]

const sortby = (arr, fn) => arr.sort((a, b) => fn(a) - fn(b))

const setup_search_index = async () => {
const search_data_href = document.head.querySelector("link[rel='pp-search-data']").getAttribute("href")
console.log(search_data_href)

const search_data = await (await fetch(search_data_href)).json()
window.search_data = search_data

console.log(search_data)

// create a search bar powered by lunr
// const search_bar = document.createElement('div')
// search_bar.id = 'search-bar'
// search_bar.innerHTML = `
// <input type="text" id="search-input" placeholder="Search...">
// <div id="search-results"></div>
// `
// document.body.appendChild(search_bar)

// create a search index
const before = Date.now()
const search_index = window.lunr(function () {
this.ref("url")

this.field("title", { boost: 10 })
this.field("tags", { boost: 5 })
this.field("text")
this.metadataWhitelist = ["position"]
search_data.forEach(function (doc) {
this.add(doc)
}, this)
})
const after = Date.now()
console.info(`lunr: Indexing ${search_data.length} documents took ${after - before}ms`)
window.search_index = search_index

return { search_data, search_index }
}

const excerpt_length = 200
const excerpt_padding = 50

const init_search = async () => {
const query = new URLSearchParams(window.location.search).get("q").replaceAll(/[\!\#\$\*\+\-\/\:\;\<\>\=\(\)\[\]\{\}\:\@\_]/g, " ")
console.warn({ query })

document.querySelector(".search-bar.big input").value = query

const { search_data, search_index } = await setup_search_index()

if (query) {
const results = search_index.search(query)
console.log(results)

const search_results = document.getElementById("search-results")

if (results.length !== 0) {
search_results.innerHTML = ""
results.forEach((result) => {
const { url, title, tags, text } = search_data.find((doc) => doc.url === result.ref)
const result_div = document.createElement("a")
result_div.classList.add("search-result")
result_div.innerHTML = `
<h3 class="title"></h3>
<p class="snippet"></p>
<p class="tags"></p>
`
console.log(root_href)
result_div.querySelector(".title").innerText = title
result_div.href = new URL(url, new URL(root_href, window.location.href)).href
result_div.querySelector(".tags").innerText = tags.join(", ")
result_div.querySelector(".snippet").innerText = text.substring(0, excerpt_length) + "..."

const text_match_positions = Object.values(result?.matchData?.metadata ?? {})
.flatMap((z) => z?.text?.position ?? [])
.sort(([a, _a], [b, _b]) => a - b)
const title_match_positions = Object.values(result?.matchData?.metadata ?? {})
.flatMap((z) => z?.title?.position ?? [])
.sort(([a, _a], [b, _b]) => a - b)

console.error(title_match_positions)
if (title_match_positions.length > 0) {
const strong_el = document.createElement("strong")
strong_el.innerText = title
result_div.querySelector(".title").innerHTML = ``
result_div.querySelector(".title").appendChild(strong_el)
}

if (text_match_positions.length > 0) {
// console.log(text_match_positions)
// console.log(find_longest_run(text_match_positions, 50))
// console.log(find_longest_run(text_match_positions, 100))
// console.log(find_longest_run(text_match_positions, 200))
// console.log(find_longest_run(text_match_positions, 300))
// console.log(find_longest_run(text_match_positions, 400))

const [start_index, num_matches] = find_longest_run(text_match_positions, excerpt_length)

const excerpt_start = text_match_positions[start_index][0]
const excerpt_end = excerpt_start + excerpt_length

const highlighted_ranges = text_match_positions.slice(start_index, start_index + num_matches)

const elements = highlighted_ranges.flatMap(([h_start, h_length], i) => {
const h_end = h_start + h_length
const word = text.slice(h_start, h_end)
const filler = text.slice(h_end, highlighted_ranges[i + 1]?.[0] ?? excerpt_end)
const word_el = document.createElement("strong")
word_el.innerText = word
return [word_el, filler]
})

const snippet_p = result_div.querySelector(".snippet")
snippet_p.innerHTML = ``
;["...", text.slice(excerpt_start - excerpt_padding, excerpt_start).trimStart(), ...elements, "..."].forEach((el) => snippet_p.append(el))
}

// text_match_positions.slice(start_index, start_index + num_matches).forEach(([start, length]) => {

search_results.appendChild(result_div)
})
} else {
search_results.innerText = `No results found for "${query}"`
}
}
}

const count = (arr, fn) => arr.reduce((a, b) => fn(a) + fn(b), 0)

const find_longest_run = (/** @type{Array<[number, number]>} */ positions, max_dist) => {
const legal_run_size = (start_index) =>
positions.slice(start_index).filter(([start, length]) => start + length < positions[start_index][0] + max_dist).length

console.warn(range(positions.length).map(legal_run_size))

const best_start = maxby(range(positions.length), legal_run_size)
const best_length = legal_run_size(best_start)
return [best_start, best_length]
}

window.init_search = init_search
55 changes: 55 additions & 0 deletions src/assets/styles/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,61 @@ body {
gap: 0.5ch;
}

/* SEARCH */

.search-result strong {
--bg-color: #73731e94;
background: var(--bg-color);
outline: 0.15em solid var(--bg-color);
border-radius: 0.1em;
}

#pages-sidebar .search-bar form {
display: flex;
flex-direction: row;
}
#pages-sidebar .search-bar input[type="search"] {
flex: 1 1 auto;
min-width: 0px;
}

a.search-result,
a.search-result:visited {
color: inherit;
display: block;
text-decoration: none;
background: var(--search-bg);
padding: 0.7rem;
margin: 2rem 1rem 2rem 0rem;
--br: 0.4em;
border-radius: var(--br);
position: relative;
}

.search-result h3 {
margin-block-start: 0;
}

.search-result .tags {
opacity: 0.6;
font-family: var(--system-fonts-mono);
}

a.search-result::before {
content: "";
display: block;
position: absolute;
z-index: -1;
--off: -3px;
top: var(--off);
right: var(--off);
left: var(--off);
bottom: var(--off);
background: var(--search-bg-accent);
transform: rotate(356.9deg) translate(0px, 0px);
border-radius: var(--br);
}

/* Markdown content */

.pages-markdown main {
Expand Down
22 changes: 22 additions & 0 deletions src/en/docs/search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
title: "Search results"
tags: []
layout: "md.jlmd"
---

<script type="module" defer>window.init_search();</script>
<div>
</div>
<h1>Search</h1>
<div class="search-bar big">
<form action="$(root_url)/en/docs/search" method="GET">
<input type="search" name="q" placeholder="Search...">
<input type=submit>
</form>
</div>

<h2>Results</h2>
<div id="search-results">
Loading...
</div>

0 comments on commit 15fd7bb

Please sign in to comment.