Add search page with lunr

This commit is contained in:
Maciej Jur 2023-04-08 23:29:39 +02:00
parent ced9aa4138
commit 7752fb6a87
12 changed files with 286 additions and 11 deletions

View file

@ -2,13 +2,17 @@ import { defineConfig } from 'astro/config';
import rehypeKatex from 'rehype-katex';
import remarkMath from 'remark-math';
import remarkEmoji from 'remark-emoji';
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
site: 'https://kamoshi.org',
trailingSlash: 'always',
markdown: {
remarkPlugins: [
[remarkEmoji as any, {accessible: true}],
remarkMath,
[(remarkEmoji as any), {accessible: true}],
remarkMath
],
rehypePlugins: [
[rehypeKatex, {output: 'mathml'}]
@ -16,5 +20,6 @@ export default defineConfig({
shikiConfig: {
theme: 'min-light'
}
}
},
integrations: [svelte()]
});

View file

@ -10,19 +10,23 @@
"astro": "astro"
},
"dependencies": {
"@astrojs/svelte": "^2.1.0",
"astro": "^2.2.0",
"dayjs": "^1.11.7",
"leaflet": "^1.9.3",
"leaflet.markercluster": "^1.5.3",
"lunr": "^2.3.9",
"rehype-katex": "^6.0.2",
"remark-emoji": "^3.1.1",
"remark-math": "^5.1.1",
"reveal.js": "^4.4.0",
"sass": "^1.61.0"
"sass": "^1.61.0",
"svelte": "^3.54.0"
},
"devDependencies": {
"@types/leaflet": "^1.9.3",
"@types/leaflet.markercluster": "^1.5.1",
"@types/lunr": "^2.3.4",
"@types/reveal.js": "^4.4.2"
}
}

View file

@ -1,6 +1,9 @@
lockfileVersion: '6.0'
dependencies:
'@astrojs/svelte':
specifier: ^2.1.0
version: 2.1.0(astro@2.2.0)(svelte@3.58.0)(typescript@4.9.5)(vite@4.2.1)
astro:
specifier: ^2.2.0
version: 2.2.0(sass@1.61.0)
@ -13,6 +16,9 @@ dependencies:
leaflet.markercluster:
specifier: ^1.5.3
version: 1.5.3(leaflet@1.9.3)
lunr:
specifier: ^2.3.9
version: 2.3.9
rehype-katex:
specifier: ^6.0.2
version: 6.0.2
@ -28,6 +34,9 @@ dependencies:
sass:
specifier: ^1.61.0
version: 1.61.0
svelte:
specifier: ^3.54.0
version: 3.58.0
devDependencies:
'@types/leaflet':
@ -36,6 +45,9 @@ devDependencies:
'@types/leaflet.markercluster':
specifier: ^1.5.1
version: 1.5.1
'@types/lunr':
specifier: ^2.3.4
version: 2.3.4
'@types/reveal.js':
specifier: ^4.4.2
version: 4.4.2
@ -106,6 +118,23 @@ packages:
prismjs: 1.29.0
dev: false
/@astrojs/svelte@2.1.0(astro@2.2.0)(svelte@3.58.0)(typescript@4.9.5)(vite@4.2.1):
resolution: {integrity: sha512-upfkscrNuZbQvqVB5EG38FPJCgHCxO/LOJLAap75rO/++c1T7ztbVru4uSYVBRJkzTDuH3TS52T8kFTVgHXx/g==}
engines: {node: '>=16.12.0'}
peerDependencies:
astro: ^2.1.0
svelte: ^3.54.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.0.4(svelte@3.58.0)(vite@4.2.1)
astro: 2.2.0(sass@1.61.0)
svelte: 3.58.0
svelte2tsx: 0.5.23(svelte@3.58.0)(typescript@4.9.5)
transitivePeerDependencies:
- supports-color
- typescript
- vite
dev: false
/@astrojs/telemetry@2.1.0:
resolution: {integrity: sha512-P3gXNNOkRJM8zpnasNoi5kXp3LnFt0smlOSUXhkynfJpTJMIDrcMbKpNORN0OYbqpKt9JPdgRN7nsnGWpbH1ww==}
engines: {node: '>=16.12.0'}
@ -648,6 +677,25 @@ packages:
tslib: 2.5.0
dev: false
/@sveltejs/vite-plugin-svelte@2.0.4(svelte@3.58.0)(vite@4.2.1):
resolution: {integrity: sha512-pjqhW00KwK2uzDGEr+yJBwut+D+4XfJO/+bHHdHzPRXn9+1Jeq5JcFHyrUiYaXgHtyhX0RsllCTm4ssAx4ZY7Q==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
svelte: ^3.54.0
vite: ^4.0.0
dependencies:
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.0
svelte: 3.58.0
svelte-hmr: 0.15.1(svelte@3.58.0)
vite: 4.2.1(sass@1.61.0)
vitefu: 0.2.4(vite@4.2.1)
transitivePeerDependencies:
- supports-color
dev: false
/@types/babel__core@7.20.0:
resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
dependencies:
@ -721,6 +769,10 @@ packages:
'@types/geojson': 7946.0.10
dev: true
/@types/lunr@2.3.4:
resolution: {integrity: sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==}
dev: true
/@types/mdast@3.0.11:
resolution: {integrity: sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==}
dependencies:
@ -1133,11 +1185,20 @@ packages:
character-entities: 2.0.2
dev: false
/dedent-js@1.0.1:
resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==}
dev: false
/deepmerge-ts@4.3.0:
resolution: {integrity: sha512-if3ZYdkD2dClhnXR5reKtG98cwyaRT1NeugQoAPTTfsOpV9kqyeiBF9Qa5RHjemb3KzD5ulqygv6ED3t5j9eJw==}
engines: {node: '>=12.4.0'}
dev: false
/deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
dev: false
/defaults@1.0.4:
resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==}
dependencies:
@ -1746,6 +1807,12 @@ packages:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
dev: false
/lower-case@2.0.2:
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
dependencies:
tslib: 2.5.0
dev: false
/lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
dependencies:
@ -1759,6 +1826,10 @@ packages:
yallist: 4.0.0
dev: false
/lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
dev: false
/magic-string@0.27.0:
resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
engines: {node: '>=12'}
@ -1766,6 +1837,13 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: false
/magic-string@0.30.0:
resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.14
dev: false
/markdown-table@3.0.3:
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
dev: false
@ -2224,6 +2302,13 @@ packages:
'@types/nlcst': 1.0.0
dev: false
/no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
dependencies:
lower-case: 2.0.2
tslib: 2.5.0
dev: false
/node-emoji@1.11.0:
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
dependencies:
@ -2329,6 +2414,13 @@ packages:
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: false
/pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
dependencies:
no-case: 3.0.4
tslib: 2.5.0
dev: false
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@ -2840,6 +2932,32 @@ packages:
engines: {node: '>= 0.4'}
dev: false
/svelte-hmr@0.15.1(svelte@3.58.0):
resolution: {integrity: sha512-BiKB4RZ8YSwRKCNVdNxK/GfY+r4Kjgp9jCLEy0DuqAKfmQtpL38cQK3afdpjw4sqSs4PLi3jIPJIFp259NkZtA==}
engines: {node: ^12.20 || ^14.13.1 || >= 16}
peerDependencies:
svelte: '>=3.19.0'
dependencies:
svelte: 3.58.0
dev: false
/svelte2tsx@0.5.23(svelte@3.58.0)(typescript@4.9.5):
resolution: {integrity: sha512-jYFnugTQRFmUpvLXPQrKzVYcW5ErT+0QCxg027Zx9BuvYefMZFuoBSTDYe7viPEFGrPPiLgT2m7f5n9khE7f7Q==}
peerDependencies:
svelte: ^3.24
typescript: ^4.1.2
dependencies:
dedent-js: 1.0.1
pascal-case: 3.1.2
svelte: 3.58.0
typescript: 4.9.5
dev: false
/svelte@3.58.0:
resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==}
engines: {node: '>= 8'}
dev: false
/synckit@0.8.5:
resolution: {integrity: sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==}
engines: {node: ^14.18.0 || >=16.0.0}
@ -2900,6 +3018,12 @@ packages:
engines: {node: '>=12.20'}
dev: false
/typescript@4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
dev: false
/typescript@5.0.3:
resolution: {integrity: sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==}
engines: {node: '>=12.20'}

View file

@ -31,6 +31,11 @@ const menu: MenuItem[] = [
identifier: 'about',
name: 'About',
url: '/about/',
},
{
identifier: 'search',
name: 'Search',
url: '/search/'
}
];
---

View file

@ -0,0 +1,96 @@
<script lang="ts">
import lunr from "lunr";
import { onMount } from "svelte";
const KEY = 'q' as const;
let value = '';
let index: lunr.Index;
let results: lunr.Index.Result[] = [];
/** Update the value so that it reflects the URL */
function sync() {
value = new URLSearchParams(window.location.search).get('q') || '';
search();
}
let debounce: number; // timer handle
/** Update browser URL with the query string */
function syncHistory(value: string) {
clearTimeout(debounce);
debounce = setTimeout(() => {
const url = new URL(window.location.href);
(value)
? url.searchParams.set(KEY, value)
: url.searchParams.delete(KEY);
window.history.pushState({}, '', url);
}, 1000);
}
async function load() {
const data = await fetch('/search.json').then(r => r.json());
index = lunr.Index.load(data);
}
function search() {
results = (!value || !index) ? [] : index.search(value);
syncHistory(value);
}
onMount(() => load().then(sync).then(search));
</script>
<article class="search">
<h1>Search</h1>
<input class="search-input" bind:value on:input={search} placeholder="Start typing here!"/>
{#if value}
<section class="results">
<div>Showing results for "{value}" ({results.length})</div>
{#each results as result}
<a class="result" href={result.ref}>
{result.ref}
</a>
{/each}
</section>
{:else}
<div>No results to show yet...</div>
{/if}
</article>
<svelte:window on:popstate={sync} />
<style lang="scss">
.search {
margin: 2em auto;
padding: 0 4em;
max-width: 52em;
.search-input {
width: 100%;
padding: 0.5em 1em;
margin-bottom: 0.5em;
box-sizing: border-box;
}
.results {
display: grid;
row-gap: 0.5em;
}
.result {
display: block;
padding: 0.5em;
background-color: white;
box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;
transition: box-shadow linear 100ms;
&:focus-within,
&:hover {
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
}
}
}
</style>

View file

@ -1,6 +1,7 @@
---
title: "Estimating package delivery time"
date: 2022-06-21T18:44:53+02:00
tags: [math, statistics]
---
In the last few months I have ordered a dozen of items from Japan. In total there were six distinct orders that were delivered to me in separate boxes. Here's the data for the time between being sent out (dispatch from outward office of exchange) from Japan and arriving (arrival at inward office of exchange) in Poland.
@ -44,7 +45,7 @@ $$
With these results we can say with 95% confidence that the mean time of delivery from Japan to Poland lies somewhere between 3.66 days and 9.27 days. We can also calculate different results for different confidence levels to get some more interesting overview:
| Confidence level | \\(t_{\alpha, n-1} \frac{s}{\sqrt{n}}\\) | Confidence interval |
| Confidence level | $t_{\alpha, n-1} \frac{s}{\sqrt{n}}$ | Confidence interval |
|------------------|------------------------------------------|----------------------------|
| 99% | 4.398820806 | (2.068077342; 10.86571895) |
| 95% | 2.804347194 | (3.662550954; 9.271245342) |

View file

@ -576,7 +576,7 @@ fn main() {
### Traits
Haskell:
```hs
```haskell
class Cool a where
giveMeNumber :: a -> Integer
```
@ -591,7 +591,7 @@ trait Cool {
---
Haskell:
```hs
```haskell
newtype Hello = Hello Integer
instance Cool Hello where
@ -610,7 +610,7 @@ impl Cool for Hello {
---
Haskell:
```hs
```haskell
main = do
let test = Hello 2
putStrLn $ show $ giveMeNumber test
@ -649,7 +649,7 @@ fn main() {
}
```
```txt
```
Hello { a: 22, b: 0, c: [] }
```

View file

@ -8,7 +8,7 @@ const posts = (await getCollection('posts'))
.sort((a, b) => a.data.date < b.data.date ? 1 : -1)
.map(entry => ({
title: entry.data.title,
slug: `/posts/${entry.slug}`,
slug: `/posts/${entry.slug}/`,
date: dayjs(entry.data.date),
}))

10
src/pages/search.astro Normal file
View file

@ -0,0 +1,10 @@
---
import Base from "../layouts/Base.astro";
import Search from "../components/search/Search.svelte";
---
<Base>
<main>
<Search client:load />
</main>
</Base>

25
src/pages/search.json.ts Normal file
View file

@ -0,0 +1,25 @@
import lunr from 'lunr';
import { getCollection } from 'astro:content';
import type { APIContext } from "astro";
const posts = await Promise.all([
getCollection('posts').then(a => a.map(p => ({...p, slug: `/posts/${p.slug}/`}))),
getCollection('aoc').then(a => a.map(p => ({...p, slug: `/aoc/${p.slug}/`}))),
getCollection('slides').then(a => a.map(p => ({...p, slug: `/slides/${p.slug}/`}))),
])
.then(array => array.flat());
const index = lunr(function() {
this.ref('slug');
this.field('body');
for (const post of posts)
this.add(post);
})
export async function get(_: APIContext) {
return {
body: JSON.stringify(index)
}
}

View file

@ -8,7 +8,7 @@ const content = await getCollection('slides');
const pages = content
.map(entry => ({
title: entry.data.title,
slug: entry.slug,
slug: `/slides/${entry.slug}/`,
date: dayjs(entry.data.date),
}));
---

5
svelte.config.js Normal file
View file

@ -0,0 +1,5 @@
import { vitePreprocess } from '@astrojs/svelte';
export default {
preprocess: vitePreprocess(),
};