1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-11-17 10:35:10 +00:00

Refactor web search index

This commit is contained in:
Brenden Matthews 2024-02-26 13:18:49 -05:00
parent 5dd632c991
commit f4a8124592
15 changed files with 564 additions and 400 deletions

3
web/.gitignore vendored
View File

@ -42,3 +42,6 @@ yarn-error.log*
# Cypress videos # Cypress videos
cypress/videos cypress/videos
# Fuse.js index (we generate this on the fly)
public/static/fuse-index.json

View File

@ -3,17 +3,14 @@ import { useRouter } from 'next/router'
import GitHub from './GitHub' import GitHub from './GitHub'
import ThemeSwitcher from './ThemeSwitcher' import ThemeSwitcher from './ThemeSwitcher'
import * as React from 'react'
import Search from './Search'
type HeaderProps = { type HeaderProps = {
name: string name: string
setDarkMode: (state: boolean) => void setDarkMode: (state: boolean) => void
searchIndex: SearchIndex
} }
import * as React from 'react'
import Search from './Search'
import { SearchIndex, SearchItem } from '../utils/search'
import Fuse, { IFuseOptions } from 'fuse.js'
interface NavLinkProps { interface NavLinkProps {
href: string href: string
name: string name: string
@ -34,20 +31,8 @@ const NavLink: React.FunctionComponent<NavLinkProps> = (props) => {
) )
} }
export default function Header({ export default function Header({ name, setDarkMode }: HeaderProps) {
name,
setDarkMode,
searchIndex,
}: HeaderProps) {
const router = useRouter() const router = useRouter()
const fuse = React.useMemo(() => {
const options: IFuseOptions<SearchItem> = {}
return new Fuse(
searchIndex.list,
options,
Fuse.parseIndex(searchIndex.index)
)
}, [searchIndex])
return ( return (
<div className="border-b-1 backdrop-blur-lg bg-white dark:bg-black bg-opacity-20 dark:bg-opacity-20 transition"> <div className="border-b-1 backdrop-blur-lg bg-white dark:bg-black bg-opacity-20 dark:bg-opacity-20 transition">
@ -65,7 +50,7 @@ export default function Header({
</div> </div>
)} )}
<div className="flex-grow" /> <div className="flex-grow" />
<Search fuse={fuse} /> <Search />
<div className="flex"> <div className="flex">
<div className="border-r mx-1 px-1 border-slate-700"> <div className="border-r mx-1 px-1 border-slate-700">
<a href="https://github.com/brndnmtthws/conky"> <a href="https://github.com/brndnmtthws/conky">

View File

@ -1,5 +1,4 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { SearchIndex } from '../utils/search'
import Footer from './Footer' import Footer from './Footer'
import Header from './Header' import Header from './Header'
@ -17,10 +16,9 @@ function darkModeDefault() {
interface LayoutProps { interface LayoutProps {
children: React.ReactNode children: React.ReactNode
searchIndex: SearchIndex
} }
export default function Layout({ children, searchIndex }: LayoutProps) { export default function Layout({ children }: LayoutProps) {
const [darkMode, setDarkMode] = useState(darkModeDefault()) const [darkMode, setDarkMode] = useState(darkModeDefault())
useEffect(() => { useEffect(() => {
@ -48,11 +46,7 @@ export default function Layout({ children, searchIndex }: LayoutProps) {
return ( return (
<div> <div>
<div className="sticky top-0 z-10 h-16"> <div className="sticky top-0 z-10 h-16">
<Header <Header name="Conky" setDarkMode={setDarkMode} />
searchIndex={searchIndex}
name="Conky"
setDarkMode={setDarkMode}
/>
</div> </div>
<div className="relative pb-4"> <div className="relative pb-4">
<div className="flex flex-col items-center max-w-3xl w-full mx-auto"> <div className="flex flex-col items-center max-w-3xl w-full mx-auto">

View File

@ -1,13 +1,11 @@
import Fuse, { FuseResult } from 'fuse.js' import Fuse, { FuseResult } from 'fuse.js'
import React, { Fragment, useCallback, useEffect, useState } from 'react' import React, { Fragment, useCallback, useEffect, useState } from 'react'
import { Search as SearchIcon } from 'react-feather' import { Search as SearchIcon } from 'react-feather'
import { SearchItem } from '../utils/search' import { SearchIndex, SearchItem } from '../utils/search'
import { Dialog, Transition, Combobox } from '@headlessui/react' import { Dialog, Transition, Combobox } from '@headlessui/react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
export interface SearchProps { export interface SearchProps {}
fuse: Fuse<SearchItem>
}
interface SearchResultProps { interface SearchResultProps {
result: FuseResult<SearchItem> result: FuseResult<SearchItem>
@ -57,12 +55,25 @@ const SearchResult: React.FunctionComponent<SearchResultProps> = (props) => {
) )
} }
const Search: React.FunctionComponent<SearchProps> = ({ fuse }) => { const Search: React.FunctionComponent<SearchProps> = () => {
const router = useRouter() const router = useRouter()
const [searchText, setSearchText] = useState('') const [searchText, setSearchText] = useState('')
const [searchResults, setSearchResults] = useState<FuseResult<SearchItem>[]>( const [searchResults, setSearchResults] = useState<FuseResult<SearchItem>[]>(
[] []
) )
const [fuse, setFuse] = React.useState<Fuse<SearchItem> | undefined>(
undefined
)
const fusePromise = async () => {
const data = await fetch('/static/fuse-index.json')
console.log(data)
const searchIndex: SearchIndex = await data.json()
console.log(searchIndex)
return new Fuse(searchIndex.list, {}, Fuse.parseIndex(searchIndex.index))
}
React.useEffect(() => {
fusePromise().then((fuse) => setFuse(fuse))
}, [])
const [isOpen, setIsOpen] = useState(false) const [isOpen, setIsOpen] = useState(false)
const handleKeyPress = useCallback( const handleKeyPress = useCallback(
@ -89,6 +100,14 @@ const Search: React.FunctionComponent<SearchProps> = ({ fuse }) => {
} }
}, [handleKeyPress]) }, [handleKeyPress])
if (!fuse) {
return (
<div className="flex items-center ml-2">
<SearchIcon size={32} />
</div>
)
}
const setSearch = (value: string) => { const setSearch = (value: string) => {
setSearchText(value) setSearchText(value)
const searchResult = fuse.search(value) const searchResult = fuse.search(value)

811
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,14 +11,15 @@
"url": "https://github.com/brndnmtthws/conky/issues" "url": "https://github.com/brndnmtthws/conky/issues"
}, },
"scripts": { "scripts": {
"dev": "next dev", "dev": "npm run write-search-index && next dev",
"dev:watch": "next-remote-watch ./documents", "dev:watch": "npm run write-search-index && next-remote-watch ./documents",
"build": "next build", "build": "npm run write-search-index && next build",
"start": "next start", "start": "npm run write-search-index && next start",
"export": "next build && next export", "export": "npm run write-search-index && next build && next export",
"lint": "next lint", "lint": "next lint",
"e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"", "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
"e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"" "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
"write-search-index": "tsx ./scripts/write-search-index.ts"
}, },
"dependencies": { "dependencies": {
"@fontsource-variable/fira-code": "^5.0.16", "@fontsource-variable/fira-code": "^5.0.16",
@ -64,6 +65,7 @@
"remark-rehype": "^11.1.0", "remark-rehype": "^11.1.0",
"start-server-and-test": "^2.0.3", "start-server-and-test": "^2.0.3",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"tsx": "^4.7.1",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
} }

View File

@ -1,17 +1,10 @@
import { Document } from '../utils/mdx-utils'
import Layout from '../components/Layout' import Layout from '../components/Layout'
import SEO from '../components/SEO' import SEO from '../components/SEO'
import { getSearchIndex, SearchIndex } from '../utils/search'
import Doggy from '../components/Doggy' import Doggy from '../components/Doggy'
interface IndexProps { export default function FourOhFour() {
documents: Document[]
searchIndex: SearchIndex
}
export default function FourOhFour({ searchIndex }: IndexProps) {
return ( return (
<Layout searchIndex={searchIndex}> <Layout>
<SEO title="Conky" description="Conky documentation" /> <SEO title="Conky" description="Conky documentation" />
<main className="w-full"> <main className="w-full">
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
@ -25,7 +18,5 @@ export default function FourOhFour({ searchIndex }: IndexProps) {
} }
export function getStaticProps() { export function getStaticProps() {
const searchIndex = getSearchIndex() return { props: {} }
return { props: { searchIndex } }
} }

View File

@ -6,16 +6,14 @@ import {
filterDesc, filterDesc,
} from '../utils/doc-utils' } from '../utils/doc-utils'
import Docs from '../components/Docs' import Docs from '../components/Docs'
import { getSearchIndex, SearchIndex } from '../utils/search'
export interface ConfigSettingsProps { export interface ConfigSettingsProps {
config_settings: Documentation config_settings: Documentation
searchIndex: SearchIndex
} }
export default function ConfigSettings(props: ConfigSettingsProps) { export default function ConfigSettings(props: ConfigSettingsProps) {
return ( return (
<Layout searchIndex={props.searchIndex}> <Layout>
<SEO <SEO
title="Conky Config settings" title="Conky Config settings"
description="Conky configuration settings" description="Conky configuration settings"
@ -34,7 +32,6 @@ export default function ConfigSettings(props: ConfigSettingsProps) {
export async function getStaticProps() { export async function getStaticProps() {
const config_settings = filterDesc(getConfigSettings()) const config_settings = filterDesc(getConfigSettings())
const searchIndex = getSearchIndex()
return { props: { config_settings, searchIndex } } return { props: { config_settings } }
} }

View File

@ -8,7 +8,6 @@ import {
import Layout from '../../components/Layout' import Layout from '../../components/Layout'
import SEO from '../../components/SEO' import SEO from '../../components/SEO'
import { GetStaticProps } from 'next' import { GetStaticProps } from 'next'
import { getSearchIndex, SearchIndex } from '../../utils/search'
import Link from 'next/link' import Link from 'next/link'
import { unified } from 'unified' import { unified } from 'unified'
import rehypeReact from 'rehype-react' import rehypeReact from 'rehype-react'
@ -27,13 +26,11 @@ interface FrontMatter {
interface DocumentPageProps { interface DocumentPageProps {
source: string source: string
frontMatter: FrontMatter frontMatter: FrontMatter
searchIndex: SearchIndex
} }
export default function DocumentPage({ export default function DocumentPage({
source, source,
frontMatter, frontMatter,
searchIndex,
}: DocumentPageProps) { }: DocumentPageProps) {
const [children, setChildren] = useState(createElement(Fragment)) const [children, setChildren] = useState(createElement(Fragment))
@ -53,7 +50,7 @@ export default function DocumentPage({
) )
return ( return (
<Layout searchIndex={searchIndex}> <Layout>
<SEO <SEO
title={`${frontMatter.title}`} title={`${frontMatter.title}`}
description={frontMatter.description} description={frontMatter.description}
@ -80,11 +77,9 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
const { source, data } = await getDocumentBySlug(params.slug as string) const { source, data } = await getDocumentBySlug(params.slug as string)
const prevDocument = getPreviousDocumentBySlug(params.slug as string) const prevDocument = getPreviousDocumentBySlug(params.slug as string)
const nextDocument = getNextDocumentBySlug(params.slug as string) const nextDocument = getNextDocumentBySlug(params.slug as string)
const searchIndex = getSearchIndex()
return { return {
props: { props: {
searchIndex,
source, source,
frontMatter: data, frontMatter: data,
prevDocument, prevDocument,

View File

@ -3,7 +3,6 @@ import { getDocuments, Document } from '../utils/mdx-utils'
import Layout from '../components/Layout' import Layout from '../components/Layout'
import ArrowIcon from '../components/ArrowIcon' import ArrowIcon from '../components/ArrowIcon'
import SEO from '../components/SEO' import SEO from '../components/SEO'
import { getSearchIndex, SearchIndex } from '../utils/search'
const pages = [ const pages = [
{ {
@ -48,12 +47,11 @@ const IndexItem: React.FunctionComponent<IndexItemProps> = (props) => {
interface IndexProps { interface IndexProps {
documents: Document[] documents: Document[]
searchIndex: SearchIndex
} }
export default function Index({ documents, searchIndex }: IndexProps) { export default function Index({ documents }: IndexProps) {
return ( return (
<Layout searchIndex={searchIndex}> <Layout>
<SEO title="Conky" description="Conky documentation" /> <SEO title="Conky" description="Conky documentation" />
<main className="w-full"> <main className="w-full">
<div className="w-full"> <div className="w-full">
@ -87,7 +85,6 @@ export default function Index({ documents, searchIndex }: IndexProps) {
export function getStaticProps() { export function getStaticProps() {
const documents = getDocuments() const documents = getDocuments()
const searchIndex = getSearchIndex()
return { props: { documents, searchIndex } } return { props: { documents } }
} }

View File

@ -2,16 +2,14 @@ import Layout from '../components/Layout'
import SEO from '../components/SEO' import SEO from '../components/SEO'
import { getLua, Documentation, filterDesc } from '../utils/doc-utils' import { getLua, Documentation, filterDesc } from '../utils/doc-utils'
import Docs from '../components/Docs' import Docs from '../components/Docs'
import { getSearchIndex, SearchIndex } from '../utils/search'
export interface LuaProps { export interface LuaProps {
lua: Documentation lua: Documentation
searchIndex: SearchIndex
} }
export default function Lua(props: LuaProps) { export default function Lua(props: LuaProps) {
return ( return (
<Layout searchIndex={props.searchIndex}> <Layout>
<SEO title="Conky Lua API" description="Conky Lua API documentation" /> <SEO title="Conky Lua API" description="Conky Lua API documentation" />
<main className="w-full"> <main className="w-full">
<div> <div>
@ -27,7 +25,6 @@ export default function Lua(props: LuaProps) {
export async function getStaticProps() { export async function getStaticProps() {
const lua = filterDesc(getLua()) const lua = filterDesc(getLua())
const searchIndex = getSearchIndex()
return { props: { lua, searchIndex } } return { props: { lua } }
} }

View File

@ -2,16 +2,14 @@ import Layout from '../components/Layout'
import SEO from '../components/SEO' import SEO from '../components/SEO'
import { getVariables, Documentation, filterDesc } from '../utils/doc-utils' import { getVariables, Documentation, filterDesc } from '../utils/doc-utils'
import Docs from '../components/Docs' import Docs from '../components/Docs'
import { getSearchIndex, SearchIndex } from '../utils/search'
export interface VariablesProps { export interface VariablesProps {
variables: Documentation variables: Documentation
searchIndex: SearchIndex
} }
export default function Variables(props: VariablesProps) { export default function Variables(props: VariablesProps) {
return ( return (
<Layout searchIndex={props.searchIndex}> <Layout>
<SEO <SEO
title="Conky Variables" title="Conky Variables"
description="Conky object variables documentation" description="Conky object variables documentation"
@ -28,7 +26,6 @@ export default function Variables(props: VariablesProps) {
export async function getStaticProps() { export async function getStaticProps() {
const variables = filterDesc(getVariables()) const variables = filterDesc(getVariables())
const searchIndex = getSearchIndex()
return { props: { variables, searchIndex } } return { props: { variables } }
} }

View File

@ -0,0 +1,7 @@
import { writeSearchIndex } from '../utils/search-serde'
async function main() {
await writeSearchIndex()
}
main()

View File

@ -0,0 +1,9 @@
import * as fs from 'fs/promises'
import { createSearchIndex } from './search'
export async function writeSearchIndex() {
await fs.mkdir('public').catch(() => {})
await fs.mkdir('public/static').catch(() => {})
const index = createSearchIndex()
await fs.writeFile('public/static/fuse-index.json', JSON.stringify(index))
}

View File

@ -1,10 +1,12 @@
import Fuse, { FuseIndexRecords } from 'fuse.js' import Fuse, { FuseIndexRecords } from 'fuse.js'
import { getConfigSettings, getLua, getVariables } from './doc-utils' import { getConfigSettings, getLua, getVariables } from './doc-utils'
export interface SearchItem { export interface SearchItem {
kind: string kind: string
name: string name: string
desc: string desc: string
} }
export interface SearchIndex { export interface SearchIndex {
index: { index: {
keys: readonly string[] keys: readonly string[]
@ -13,7 +15,7 @@ export interface SearchIndex {
list: SearchItem[] list: SearchItem[]
} }
export function getSearchIndex() { export function createSearchIndex(): SearchIndex {
const cs: SearchItem[] = getConfigSettings().values.map((v) => ({ const cs: SearchItem[] = getConfigSettings().values.map((v) => ({
kind: 'config', kind: 'config',
name: v.name, name: v.name,