(search) add search for page titles and sometimes contents

This commit is contained in:
Valentyne Stigloher 2024-12-26 19:16:49 +01:00
parent 6d66f56688
commit bceb2c261d
3 changed files with 249 additions and 4 deletions

View File

@ -6,6 +6,7 @@ const props = defineProps<{
}>(); }>();
const iconBySearchKind: Record<SearchDocument['kind'], string> = { const iconBySearchKind: Record<SearchDocument['kind'], string> = {
page: 'file',
pronoun: 'tags', pronoun: 'tags',
noun: 'book', noun: 'book',
source: 'books', source: 'books',

View File

@ -30,6 +30,7 @@ const translator = new Translator(translations, baseTranslations, global.config)
interface SearchKind { interface SearchKind {
kind: SearchDocument['kind']; kind: SearchDocument['kind'];
options?: Partial<Options<SearchDocument>>;
getDocuments(config: Config): Promise<SearchDocument[]>; getDocuments(config: Config): Promise<SearchDocument[]>;
transformDocument?(transformed: SearchDocument, termsByField: Record<string, string[]>): void; transformDocument?(transformed: SearchDocument, termsByField: Record<string, string[]>): void;
} }
@ -40,7 +41,7 @@ interface LoadedSearchKind {
index: MiniSearch<SearchDocument>; index: MiniSearch<SearchDocument>;
} }
const MINISEARCH_OPTIONS: Options<SearchDocument> = { const DEFAULT_OPTIONS: Options<SearchDocument> = {
fields: ['title', 'titleSmall', 'content'], fields: ['title', 'titleSmall', 'content'],
storeFields: ['kind'], storeFields: ['kind'],
}; };
@ -50,7 +51,7 @@ const getSearchDocumentsAndIndex = defineCachedFunction(async (
config: Config, config: Config,
): Promise<{ documents: SearchDocument[]; index: MiniSearch<SearchDocument> | AsPlainObject }> => { ): Promise<{ documents: SearchDocument[]; index: MiniSearch<SearchDocument> | AsPlainObject }> => {
const documents = await kind.getDocuments(config); const documents = await kind.getDocuments(config);
const index = new MiniSearch<SearchDocument>(MINISEARCH_OPTIONS); const index = new MiniSearch<SearchDocument>({ ...kind.options, ...DEFAULT_OPTIONS });
index.addAll(documents); index.addAll(documents);
return { documents, index }; return { documents, index };
}, { }, {
@ -62,7 +63,7 @@ const getSearchDocumentsAndIndex = defineCachedFunction(async (
const loadSearchDocumentsAndIndex = async (kind: SearchKind, config: Config): Promise<LoadedSearchKind> => { const loadSearchDocumentsAndIndex = async (kind: SearchKind, config: Config): Promise<LoadedSearchKind> => {
const { documents, index } = await getSearchDocumentsAndIndex(kind, config); const { documents, index } = await getSearchDocumentsAndIndex(kind, config);
if (!(index instanceof MiniSearch)) { if (!(index instanceof MiniSearch)) {
return { kind, documents, index: MiniSearch.loadJS(index, MINISEARCH_OPTIONS) }; return { kind, documents, index: MiniSearch.loadJS(index, { ...kind.options, ...DEFAULT_OPTIONS }) };
} }
return { kind, documents, index }; return { kind, documents, index };
}; };
@ -138,6 +139,249 @@ const transformResult = (indices: Map<SearchDocument['kind'],
}; };
const kinds: SearchKind[] = [ const kinds: SearchKind[] = [
{
kind: 'page',
options: {
searchOptions: {
boost: { title: 2 },
},
},
async getDocuments(config: Config): Promise<SearchDocument[]> {
const documents: SearchDocument[] = [];
const addDocument = ({ url, title, content }: { url: string; title: string; content?: string }) => {
documents.push({
id: documents.length,
kind: this.kind,
url,
title,
content: content ?? '',
});
};
addDocument({
url: '/',
title: translator.translate('home.link'),
content: [
translator.translate('home.intro'),
translator.translate('home.why'),
...translator.get<string[]>('home.about').map((text) => clearLinkedText(text, false)),
].join(' '),
});
if (config.pronouns.enabled) {
addDocument({
url: `/${encodeURIComponent(config.pronouns.route)}`,
title: translator.translate('pronouns.prononus'),
content: '',
});
}
if (config.nouns.enabled) {
addDocument({
url: `/${encodeURIComponent(config.nouns.route)}`,
title: translator.translate('nouns.headerLonger'),
content: [
translator.translate('nouns.description'),
...translator.get<string[]>('nouns.intro').map((text) => clearLinkedText(text, false)),
].join(' '),
});
}
if (config.sources.enabled) {
addDocument({
url: `/${encodeURIComponent(config.sources.route)}`,
title: translator.translate('sources.headerLonger'),
content: translator.translate('sources.subheader'),
});
}
if (config.faq.enabled) {
addDocument({
url: `/${encodeURIComponent(config.faq.route)}`,
title: translator.translate('faq.header'),
content: translator.translate('faq.headerLong'),
});
}
if (config.links.enabled) {
addDocument({
url: `/${encodeURIComponent(config.links.route)}`,
title: translator.translate('links.header'),
content: translator.translate('links.headerLong'),
});
if (config.links.academicRoute) {
addDocument({
url: `/${encodeURIComponent(config.links.academicRoute)}`,
title: translator.translate('links.academic.header'),
content: translator.get<string[]>('links.academic.intro')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.links.translinguisticsRoute) {
addDocument({
url: `/${encodeURIComponent(config.links.translinguisticsRoute)}`,
title: translator.translate('links.translinguistics.headerLong'),
content: translator.get<string[]>('links.translinguistics.intro')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.links.mediaRoute) {
addDocument({
url: `/${encodeURIComponent(config.links.mediaRoute)}`,
title: translator.translate('links.media.header'),
content: '',
});
}
if (config.links.zine?.enabled) {
addDocument({
url: `/${encodeURIComponent(config.links.zine.route)}`,
title: translator.translate('links.zine.headerLong'),
content: translator.get<string[]>('links.zine.info')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.links.blog) {
addDocument({
url: `/${encodeURIComponent(config.links.blogRoute)}`,
title: translator.translate('links.blog'),
content: '',
});
}
if (config.terminology.enabled) {
addDocument({
url: `/${encodeURIComponent(config.terminology.route)}`,
title: translator.translate('terminology.headerLong'),
content: translator.get<string[]>('terminology.info')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.calendar?.enabled) {
addDocument({
url: `/${encodeURIComponent(config.calendar.route)}`,
title: translator.translate('calendar.headerLong'),
content: '',
});
}
if (config.census.enabled) {
addDocument({
url: `/${encodeURIComponent(config.census.route)}`,
title: translator.translate('census.headerLong'),
content: '',
});
}
if (config.inclusive.enabled) {
addDocument({
url: `/${encodeURIComponent(config.inclusive.route)}`,
title: translator.translate('inclusive.headerLong'),
content: translator.get<string[]>('inclusive.info')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.names.enabled) {
addDocument({
url: `/${encodeURIComponent(config.names.route)}`,
title: translator.translate('names.headerLong'),
content: [
translator.translate('inclusive.description'),
...translator.get<string[]>('inclusive.info')
.map((text) => clearLinkedText(text, false)),
].join(' '),
});
}
if (config.people.enabled) {
addDocument({
url: `/${encodeURIComponent(config.people.route)}`,
title: translator.translate('people.headerLonger'),
content: [
translator.translate('people.description'),
...translator.get<string[]>('people.info')
.map((text) => clearLinkedText(text, false)),
].join(' '),
});
}
if (config.contact.enabled && config.contact.team.enabled) {
addDocument({
url: `/${encodeURIComponent(config.contact.team.route)}`,
title: translator.translate('contact.team.name'),
content: [
translator.translate('contact.team.description'),
translator.translate('contact.contribute.header'),
translator.translate('home.mission.header'),
translator.translate('home.mission.summary'),
translator.translate('home.mission.freedom'),
translator.translate('home.mission.respect'),
translator.translate('home.mission.inclusivity'),
`${translator.translate('contact.contribute.intro')}:`,
...['entries', 'translations', 'version', 'technical'].map((area) => {
const header = translator.translate(`contact.contribute.${area}.header`);
const description = translator.translate(`contact.contribute.${area}.description`);
return `${header}: ${clearLinkedText(description, false)}`;
}),
].join(' '),
});
}
if (config.workshops?.enabled) {
addDocument({
url: `/${encodeURIComponent(config.workshops.route)}`,
title: translator.translate('workshops.headerLong'),
content: translator.get<string[]>('workshops.content')
.map((text) => clearLinkedText(text, false))
.join(' '),
});
}
if (config.contact.enabled) {
addDocument({
url: `/${encodeURIComponent(config.contact.route)}`,
title: translator.translate('contact.header'),
content: [
translator.translate('contact.faq'),
translator.translate('contact.technical'),
translator.translate('contact.hate'),
translator.translate('contact.language'),
].join(' '),
});
}
addDocument({
url: 'https://shop.pronouns.page',
title: translator.translate('contact.groups.shop'),
content: '',
});
}
if (config.english.enabled) {
addDocument({
url: `/${encodeURIComponent(config.english.route)}`,
title: translator.translate('links.english.header'),
content: [
translator.translate('english.headerLonger'),
translator.translate('english.description'),
...translator.get<string[]>('english.intro')
.map((text) => clearLinkedText(text, false)),
].join(' '),
});
}
return documents;
},
},
{ {
kind: 'pronoun', kind: 'pronoun',
async getDocuments(config: Config): Promise<SearchDocument[]> { async getDocuments(config: Config): Promise<SearchDocument[]> {

View File

@ -6,7 +6,7 @@ interface SearchDocumentImage {
export interface SearchDocument { export interface SearchDocument {
id: number; id: number;
kind: 'pronoun' | 'noun' | 'source' | 'link' | 'faq' | 'blog' | 'term' | 'inclusive' | 'calendar'; kind: 'page' | 'pronoun' | 'noun' | 'source' | 'link' | 'faq' | 'blog' | 'term' | 'inclusive' | 'calendar';
url: string; url: string;
title: string; title: string;
titleSmall?: string | undefined; titleSmall?: string | undefined;