<commit_before>import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

import Select from 'react-select/creatable'

const useSearch = ({ items, setMemoryIndex, indexedKeywords }) => {
  const router = useRouter()
  const [keyword, setKeyword] = useState(router.query.keyword || '')
  const [selectedOption, setSelectedOption] = useState(null)

  const getShareUrlStem = () => {
    if (router.asPath.includes('keyword=')) {
      return router.asPath
      // const urlParts = new URL(window.location)
      // urlParts.searchParams.set('keyword', keyword)
      // return urlParts.toString()
    }
    return `${router.asPath}?keyword=${keyword}`
  }

  function handleSubmit(event) {
    event.preventDefault()
    setKeyword(selectedOption.value)
    setMemoryIndex?.(0)
  }

  const keywordResultLabel = keyword === '' ? null : (<> for &quot;{keyword}&quot;</>)
  const getSearchBox = (filtered) => (
    <form onSubmit={handleSubmit}>
      <h3>Search results {filtered?.length} of {items?.length}{keywordResultLabel}</h3>
      <nav>{getShareUrlStem()}</nav>
      <Select
        isClearable
        options={indexedKeywords}
        defaultValue={selectedOption}
        onChange={setSelectedOption}
      />
      <input type="submit" value="Filter" />
      <code>`&&` is AND; `||` is OR; for example `breakfast||lunch`</code>
    </form>
  )

  const defaultReturn = {
    filtered: items,
    keyword: '',
    setKeyword,
    searchBox: getSearchBox(items),
  }

  useEffect(() => {
    if (router.isReady && router.query.keyword) {
      setKeyword(router.query.keyword)
    }
  }, [router.isReady])

  if (!router.isReady) {
    return defaultReturn
  }

  const AND_OPERATOR = '&&'
  const OR_OPERATOR = '||'
  const normalizeCorpus = (corpus) => {
    const corpusWithoutAccentLow = corpus.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
    return (k) => {
      const keywordWithoutAccentLow = k.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
      return corpusWithoutAccentLow.indexOf(keywordWithoutAccentLow) !== -1
    }
  }
  const filtered = items.filter((item) => {
    if (!keyword) return true
    const findMatch = normalizeCorpus(item.corpus)
    if (keyword.includes(AND_OPERATOR)) {
      return keyword.split(AND_OPERATOR).every(findMatch)
    }
    return keyword.split(OR_OPERATOR).some(findMatch)
  })

  return {
    filtered,<commit_msg>fix(Next > useSearch): Support URL QS to change keyword; Fix clearable<commit_after>import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'

import Select from 'react-select/creatable'

const useSearch = ({ items, setMemoryIndex, indexedKeywords }) => {
  const router = useRouter()
  const [keyword, setKeyword] = useState(router.query.keyword || '')
  const [selectedOption, setSelectedOption] = useState(null)

  const getShareUrlStem = () => {
    if (router.asPath.includes('keyword=')) {
      return decodeURI(router.asPath)
      // const urlParts = new URL(window.location)
      // urlParts.searchParams.set('keyword', keyword)
      // return urlParts.toString()
    }
    return `${router.asPath}?keyword=${keyword}`
  }

  function handleSubmit(event) {
    event.preventDefault()
    setKeyword(selectedOption?.value ?? '')
    setMemoryIndex?.(0)
  }

  const keywordResultLabel = keyword === '' ? null : (<> for &quot;{keyword}&quot;</>)
  const getSearchBox = (filtered) => (
    <form onSubmit={handleSubmit}>
      <h3>Search results {filtered?.length} of {items?.length}{keywordResultLabel}</h3>
      <nav>{getShareUrlStem()}</nav>
      <Select
        isClearable
        options={indexedKeywords}
        value={selectedOption}
        onChange={setSelectedOption}
      />
      <input type="submit" value="Filter" />
      <code>`&&` is AND; `||` is OR; for example `breakfast||lunch`</code>
    </form>
  )

  const defaultReturn = {
    filtered: items,
    keyword: '',
    setKeyword,
    searchBox: getSearchBox(items),
  }

  useEffect(() => {
    if (router.isReady && router.query.keyword) {
      setKeyword(router.query.keyword)
      setSelectedOption({ label: router.query.keyword, value: router.query.keyword })
    }
  }, [router.isReady])

  if (!router.isReady) {
    return defaultReturn
  }

  const AND_OPERATOR = '&&'
  const OR_OPERATOR = '||'
  const normalizeCorpus = (corpus) => {
    const corpusWithoutAccentLow = corpus.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
    return (k) => {
      const keywordWithoutAccentLow = k.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase()
      return corpusWithoutAccentLow.indexOf(keywordWithoutAccentLow) !== -1
    }
  }
  const filtered = items.filter((item) => {
    if (!keyword) return true
    const findMatch = normalizeCorpus(item.corpus)
    if (keyword.includes(AND_OPERATOR)) {
      return keyword.split(AND_OPERATOR).every(findMatch)
    }
    return keyword.split(OR_OPERATOR).some(findMatch)
  })

  return {
    filtered,
{'fraction_non_alphanumeric': 0.1515332536837913, 'fraction_numerical': 0.007168458781362007, 'mean_word_length': 3.4968666069829903, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1334645', 'n_tokens_mistral': 1582, 'n_tokens_neox': 1559, 'n_words': 293}