mirror of
https://github.com/cupcakearmy/nicco.io.git
synced 2024-12-21 23:56:26 +00:00
search
This commit is contained in:
parent
92a4fb9d94
commit
deb89701f7
24
package-lock.json
generated
24
package-lock.json
generated
@ -8,12 +8,14 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"dayjs": "^1.10.5",
|
"dayjs": "^1.10.5",
|
||||||
"highlight.js": "^11.0.0",
|
"highlight.js": "^11.0.0",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
"svelte-cloudinary": "^0.2.3"
|
"svelte-cloudinary": "^0.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-static": "next",
|
"@sveltejs/adapter-static": "next",
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "next",
|
||||||
"@types/highlight.js": "^10.1.0",
|
"@types/highlight.js": "^10.1.0",
|
||||||
|
"@types/lunr": "^2.3.4",
|
||||||
"svelte": "^3.34.0",
|
"svelte": "^3.34.0",
|
||||||
"svelte-check": "^2.0.0",
|
"svelte-check": "^2.0.0",
|
||||||
"svelte-preprocess": "^4.0.0",
|
"svelte-preprocess": "^4.0.0",
|
||||||
@ -98,6 +100,12 @@
|
|||||||
"highlight.js": "*"
|
"highlight.js": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/lunr": {
|
||||||
|
"version": "2.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz",
|
||||||
|
"integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "16.7.1",
|
"version": "16.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
|
||||||
@ -550,6 +558,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.25.7",
|
"version": "0.25.7",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
@ -1028,6 +1041,12 @@
|
|||||||
"highlight.js": "*"
|
"highlight.js": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/lunr": {
|
||||||
|
"version": "2.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.4.tgz",
|
||||||
|
"integrity": "sha512-j4x4XJwZvorEUbA519VdQ5b9AOU9TSvfi8tvxMAfP8XzNLtFex7A8vFQwqOx3WACbV0KMXbACV3cZl4/gynQ7g==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "16.7.1",
|
"version": "16.7.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz",
|
||||||
@ -1362,6 +1381,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
|
"lunr": {
|
||||||
|
"version": "2.3.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||||
|
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow=="
|
||||||
|
},
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.25.7",
|
"version": "0.25.7",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"@sveltejs/adapter-static": "next",
|
"@sveltejs/adapter-static": "next",
|
||||||
"@sveltejs/kit": "next",
|
"@sveltejs/kit": "next",
|
||||||
"@types/highlight.js": "^10.1.0",
|
"@types/highlight.js": "^10.1.0",
|
||||||
|
"@types/lunr": "^2.3.4",
|
||||||
"svelte": "^3.34.0",
|
"svelte": "^3.34.0",
|
||||||
"svelte-check": "^2.0.0",
|
"svelte-check": "^2.0.0",
|
||||||
"svelte-preprocess": "^4.0.0",
|
"svelte-preprocess": "^4.0.0",
|
||||||
@ -22,6 +23,7 @@
|
|||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"dayjs": "^1.10.5",
|
"dayjs": "^1.10.5",
|
||||||
"highlight.js": "^11.0.0",
|
"highlight.js": "^11.0.0",
|
||||||
|
"lunr": "^2.3.9",
|
||||||
"svelte-cloudinary": "^0.2.3"
|
"svelte-cloudinary": "^0.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
<script>
|
<script lang="ts" context="module">
|
||||||
export let result
|
export type SearchResultItem = {
|
||||||
|
ref: string
|
||||||
|
score: number
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export let result: SearchResultItem
|
||||||
|
|
||||||
const [type, slug] = result.ref.split('/')
|
const [type, slug] = result.ref.split('/')
|
||||||
let href = '/'
|
let href = '/'
|
||||||
@ -7,13 +14,15 @@
|
|||||||
$: {
|
$: {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'works':
|
case 'works':
|
||||||
|
href = `${type}/${slug}`
|
||||||
|
break
|
||||||
case 'projects':
|
case 'projects':
|
||||||
href = `${type}`
|
href = `${type}`
|
||||||
break
|
break
|
||||||
case 'post':
|
case 'posts':
|
||||||
href = `/blog/${slug}`
|
href = `/blog/${slug}`
|
||||||
break
|
break
|
||||||
case 'page':
|
case 'pages':
|
||||||
href = `/${slug}`
|
href = `/${slug}`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
53
src/routes/api/search.json.ts
Normal file
53
src/routes/api/search.json.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import lunr from 'lunr'
|
||||||
|
import type { RequestHandler } from '@sveltejs/kit'
|
||||||
|
import { BaseAttributes, Call, gql, Page } from '$lib/api'
|
||||||
|
|
||||||
|
function removeHTML(s: string) {
|
||||||
|
return s.replace(/<.*?>|\s+|&#\d+;/g, ' ').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertForIdx(type: string, items: Page[]) {
|
||||||
|
const keys: (keyof Page)[] = ['title', 'content', 'slug']
|
||||||
|
return items.map((item) => ({
|
||||||
|
url: `${type}/${item.slug}`,
|
||||||
|
data: keys.map((field) => removeHTML(item[field] ?? '')).join(' '),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getAll() {
|
||||||
|
const all = await Call<Record<'posts' | 'projects' | 'works', { nodes: Page[] }>>(gql`
|
||||||
|
query {
|
||||||
|
posts(first: 1000) {
|
||||||
|
nodes {
|
||||||
|
${BaseAttributes}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
projects(first: 1000) {
|
||||||
|
nodes {
|
||||||
|
${BaseAttributes}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
works(first: 1000) {
|
||||||
|
nodes {
|
||||||
|
${BaseAttributes}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
|
export const get: RequestHandler = async () => {
|
||||||
|
const all = await getAll()
|
||||||
|
const converted = Object.entries(all)
|
||||||
|
.map(([type, data]) => convertForIdx(type, data.nodes))
|
||||||
|
.flat()
|
||||||
|
|
||||||
|
const idx = lunr(function () {
|
||||||
|
this.ref('url')
|
||||||
|
this.field('data')
|
||||||
|
converted.forEach((doc) => this.add(doc))
|
||||||
|
})
|
||||||
|
|
||||||
|
return { body: idx }
|
||||||
|
}
|
@ -20,10 +20,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Works</title>
|
<title>Blog - {data.title}</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<SimplePage title={data.title}>
|
<SimplePage title={data.title} readable>
|
||||||
<PostAttributes post={data} full />
|
<PostAttributes post={data} full />
|
||||||
{#if data.content}
|
{#if data.content}
|
||||||
<WpAdapter content={data.content} />
|
<WpAdapter content={data.content} />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
import type { Load } from '@sveltejs/kit'
|
import type { Load } from '@sveltejs/kit'
|
||||||
|
|
||||||
|
export const prerender = true
|
||||||
export const load: Load = async ({ fetch }) => {
|
export const load: Load = async ({ fetch }) => {
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
@ -1 +1,80 @@
|
|||||||
Hello
|
<script lang="ts" context="module">
|
||||||
|
import type { Load } from '@sveltejs/kit'
|
||||||
|
|
||||||
|
export const load: Load = async ({ fetch }) => {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
prebuilt: await fetch('/api/search.json').then((r) => r.json()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import lunr from 'lunr'
|
||||||
|
import { onMount } from 'svelte'
|
||||||
|
|
||||||
|
import type { SearchResultItem } from '$lib/components/SearchResult.svelte'
|
||||||
|
import SearchResult from '$lib/components/SearchResult.svelte'
|
||||||
|
import SimplePage from '$lib/components/SimplePage.svelte'
|
||||||
|
|
||||||
|
export let prebuilt: any
|
||||||
|
let needle: string | null = null
|
||||||
|
let results: SearchResultItem[] = []
|
||||||
|
|
||||||
|
const idx = lunr.Index.load(prebuilt)
|
||||||
|
|
||||||
|
async function search(needle: string) {
|
||||||
|
if (!needle || !idx) {
|
||||||
|
results = []
|
||||||
|
} else {
|
||||||
|
let found = idx.search(needle + '~1')
|
||||||
|
if (!found.length) found = idx.search(needle + '*')
|
||||||
|
results = found.slice(0, 20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (needle) {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.history.replaceState(null, '', `/search?q=${needle ?? ''}`)
|
||||||
|
}
|
||||||
|
search(needle)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
needle = new URLSearchParams(window.location.search).get('q')
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SimplePage title="Search" expanded={false}>
|
||||||
|
<input bind:value={needle} placeholder="needle" />
|
||||||
|
{#if needle}
|
||||||
|
<ul>
|
||||||
|
{#each results as result (result.ref)}
|
||||||
|
<SearchResult {result} />
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
{:else}
|
||||||
|
<p>Start typing...</p>
|
||||||
|
{/if}
|
||||||
|
</SimplePage>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
font-size: 1em;
|
||||||
|
width: 100%;
|
||||||
|
outline: none;
|
||||||
|
border: 2px solid var(--clr-primary);
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-top: 2em;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user