feat: opt-in Journal search page and JournalSearchBox

- journal.routes.search (off by default) registers journal-search, the
  results page for ?q= in the API's relevance order (no sort is sent),
  server-rendered with a Polish-aware result count.
- JournalSearchBox: an icon button and a WAI-ARIA combobox with debounced
  ?fields=suggest suggestions, stale answers dropped, query words marked
  ignoring diacritics; a plain GET form without JS.
- The module keeps the search path out of the route cache and the sitemap
  and marks it noindex, follow.
- Client: suggestPosts and the fields option; useJournalLinks.searchLink.
This commit is contained in:
Jakub Zych
2026-10-30 18:14:05 +01:00
parent e0cc87118c
commit 102da43f76
22 changed files with 1170 additions and 14 deletions

View File

@@ -20,11 +20,11 @@ export function useJournalApi(): JournalClient {
const peer = event?.node?.req?.socket?.remoteAddress
const forwarded = recorded || [incoming, peer].filter((v): v is string => typeof v === 'string' && v !== '').join(', ')
const headers: Record<string, string> = forwarded ? { 'x-forwarded-for': forwarded } : {}
const fetcher: JournalFetcher = (path, query) => $fetch(path, { baseURL: base, query, headers })
const fetcher: JournalFetcher = (path, query, init) => $fetch(path, { baseURL: base, query, headers, signal: init?.signal })
return createJournalClient(fetcher)
}
const base = (config.public.journal as { apiBase: string }).apiBase
const fetcher: JournalFetcher = (path, query) => $fetch(path, { baseURL: base, query })
const fetcher: JournalFetcher = (path, query, init) => $fetch(path, { baseURL: base, query, signal: init?.signal })
return createJournalClient(fetcher)
}

View File

@@ -19,5 +19,7 @@ export function useJournalLinks() {
tagLink: (slug: string) => link('tag', { tag: slug }),
authorLink: (slug: string) => link('author', { author: slug }),
indexLink: (page: number = 1) => link('index', undefined, page > 1 ? { page: String(page) } : undefined),
/** The search page for `q`, or null when journal.routes.search is false. */
searchLink: (q?: string) => link('search', undefined, q ? { q } : undefined),
}
}