add search

This commit is contained in:
Niccolo Borgioli 2024-12-02 10:45:22 +01:00
parent f1449a157c
commit 940cd5cea9
8 changed files with 128 additions and 7 deletions

View File

@ -1,3 +1,3 @@
- Search
- Tag count - Tag count
- Link in contact to status monitor - title for pages
- check header meta info

View File

@ -19,6 +19,7 @@
"@fontsource-variable/playfair-display": "^5.1.0", "@fontsource-variable/playfair-display": "^5.1.0",
"@iconify-json/ion": "^1.2.1", "@iconify-json/ion": "^1.2.1",
"astro": "^4.16.13", "astro": "^4.16.13",
"fuse.js": "^7.0.0",
"mdast-util-to-string": "^4.0.0", "mdast-util-to-string": "^4.0.0",
"reading-time": "^1.5.0", "reading-time": "^1.5.0",
"rehype-autolink-headings": "^7.1.0", "rehype-autolink-headings": "^7.1.0",

9
pnpm-lock.yaml generated
View File

@ -38,6 +38,9 @@ importers:
astro: astro:
specifier: ^4.16.13 specifier: ^4.16.13
version: 4.16.13(rollup@4.27.3)(sass@1.81.0)(stylus@0.64.0)(typescript@5.6.3) version: 4.16.13(rollup@4.27.3)(sass@1.81.0)(stylus@0.64.0)(typescript@5.6.3)
fuse.js:
specifier: ^7.0.0
version: 7.0.0
mdast-util-to-string: mdast-util-to-string:
specifier: ^4.0.0 specifier: ^4.0.0
version: 4.0.0 version: 4.0.0
@ -1225,6 +1228,10 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin] os: [darwin]
fuse.js@7.0.0:
resolution: {integrity: sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==}
engines: {node: '>=10'}
gensync@1.0.0-beta.2: gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -3675,6 +3682,8 @@ snapshots:
fsevents@2.3.3: fsevents@2.3.3:
optional: true optional: true
fuse.js@7.0.0: {}
gensync@1.0.0-beta.2: {} gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {} get-caller-file@2.0.5: {}

View File

@ -5,6 +5,7 @@ export function remarkReadingTime() {
return function (tree, { data }) { return function (tree, { data }) {
const textOnPage = toString(tree) const textOnPage = toString(tree)
const readingTime = getReadingTime(textOnPage) const readingTime = getReadingTime(textOnPage)
data.astro.frontmatter.text = textOnPage
data.astro.frontmatter.readingTime = readingTime data.astro.frontmatter.readingTime = readingTime
} }
} }

View File

@ -45,5 +45,9 @@ const description = 'Welcome to my website!'
<meta property="twitter:description" content={description} /> <meta property="twitter:description" content={description} />
<meta property="twitter:image" content={new URL(image, Astro.url)} /> <meta property="twitter:image" content={new URL(image, Astro.url)} />
<script async defer src="https://spectare.nicco.io/unicorn.js" data-website-id="bc7525c5-6928-49e1-9255-aca296947def" <script
></script> is:inline
async
defer
src="https://spectare.nicco.io/unicorn.js"
data-website-id="bc7525c5-6928-49e1-9255-aca296947def"></script>

View File

@ -1,4 +1,5 @@
--- ---
import SearchIcon from '~icons/ion/search-outline'
const { pathname } = Astro.url const { pathname } = Astro.url
const routes = [ const routes = [
@ -14,11 +15,11 @@ const routes = [
<h1 class:list={{ active: pathname === '/' }}>NB</h1> <h1 class:list={{ active: pathname === '/' }}>NB</h1>
</a> </a>
<ul> <ul>
<!-- <li> <li>
<a href="/search"> <a href="/search">
<Icon icon="search-outline" /> <SearchIcon />
</a> </a>
</li> --> </li>
{ {
routes.map(({ href, name }) => ( routes.map(({ href, name }) => (
<li> <li>

View File

@ -0,0 +1,66 @@
<script lang="ts">
import Fuse from 'fuse.js'
const { entries } = $props()
const fuse = new Fuse(entries, {
keys: ['text', 'url', 'extra'],
includeScore: true,
includeMatches: false,
minMatchCharLength: 2,
threshold: 0.5,
})
let needle = $state('')
const results = $derived(fuse.search(needle))
</script>
<input bind:value={needle} />
<ol>
{#each results as result}
<li>
<a href={result.item.url}>
<span class="meta">
{result.item.type}
</span>
<span class="meta">
{(1 - result.score).toFixed(2)}
</span>
{result.item.title}
</a>
</li>
{/each}
</ol>
<style lang="scss">
input {
background: white;
border: 2px solid var(--clr-primary);
border-bottom-width: 4px;
padding: 1rem;
&:focus,
&:hover {
outline: none;
border-color: var(--clr-secondary);
}
}
a {
display: flex;
flex-direction: row;
align-items: start;
gap: 0.5rem;
}
li {
margin-bottom: 1rem;
}
.meta {
background: var(--clr-secondary);
width: fit-content;
padding: 0 0.25rem;
}
</style>

39
src/pages/search.astro Normal file
View File

@ -0,0 +1,39 @@
---
import { getCollection } from 'astro:content'
import PageSearch from '../components/PageSearch.svelte'
import PageWithTitle from '../layouts/PageWithTitle.astro'
type Entry = { url: string; type: 'post' | 'page'; title: string; text: string; extra?: any }
const entries: Entry[] = []
const posts = await getCollection('blog')
for (const post of posts) {
const rendered = await post.render()
const text = rendered.remarkPluginFrontmatter.text
entries.push({
url: `/blog/${post.slug}`,
type: 'post',
title: post.data.title,
text,
extra: post.data,
})
}
const pages = await getCollection('page')
for (const page of pages) {
const rendered = await page.render()
const text = rendered.remarkPluginFrontmatter.text
entries.push({
url: `/${page.slug}`,
type: 'page',
title: page.data.title,
text,
extra: page.data,
})
}
---
<PageWithTitle title="Search">
<PageSearch entries={entries} client:only="svelte" />
</PageWithTitle>