diff --git a/components/search/SearchItemTerm.vue b/components/search/SearchItemTerm.vue
new file mode 100644
index 000000000..5d682e554
--- /dev/null
+++ b/components/search/SearchItemTerm.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
diff --git a/pages/search.vue b/pages/search.vue
index 84a4cd1fd..e9644e357 100644
--- a/pages/search.vue
+++ b/pages/search.vue
@@ -38,6 +38,7 @@ const searchInput = useTemplateRef('searchInput');
+
diff --git a/server/api/search.get.ts b/server/api/search.get.ts
index 6cf7152ca..4835db47e 100644
--- a/server/api/search.get.ts
+++ b/server/api/search.get.ts
@@ -9,6 +9,7 @@ import type { Config } from '~/locale/config.ts';
import { getPosts, type PostMetadata } from '~/server/blog.ts';
import { getNounEntries } from '~/server/express/nouns.ts';
import { getSourcesEntries } from '~/server/express/sources.ts';
+import { getTermsEntries } from '~/server/express/terms.ts';
import { loadSuml, loadSumlFromBase } from '~/server/loader.ts';
import { rootDir } from '~/server/paths.ts';
import { parsePronouns } from '~/src/buildPronoun.ts';
@@ -430,6 +431,77 @@ class SearchIndexBlog extends SearchIndex
}
}
+interface SearchDocumentTerm {
+ id: number;
+ type: SearchIndexTerm['TYPE'];
+ url: string;
+ title: string;
+ image: string | undefined;
+ content: string;
+}
+
+export type SearchResultTerm = SearchDocumentTerm;
+
+class SearchIndexTerm extends SearchIndex {
+ TYPE = 'term' as const;
+
+ constructor() {
+ super(['url', 'title', 'content']);
+ }
+
+ async getDocuments(config: Config): Promise {
+ if (!config.terminology.enabled) {
+ return [];
+ }
+
+ const runtimeConfig = useRuntimeConfig();
+
+ const base = encodeURIComponent(config.terminology.route);
+
+ const db = useDatabase();
+ const terms = await getTermsEntries(db, () => false);
+ return terms.map((term, id): SearchDocumentTerm => {
+ const title = term.term.replaceAll('|', ', ');
+
+ let content = '';
+ if (term.original) {
+ content += `${clearLinkedText(term.original.replaceAll('|', ';'), false)}`;
+ }
+ content += ` ${clearLinkedText(term.definition, false)}`;
+
+ let image = undefined;
+ const flags = JSON.parse(term.flags);
+ if (flags.length > 0) {
+ image = `/flags/${flags[0]}.png`;
+ } else if (term.images) {
+ image = buildImageUrl(runtimeConfig.public.cloudfront, term.images.split(',')[0], 'flag');
+ }
+
+ return {
+ id,
+ type: this.TYPE,
+ url: `/${base}?filter=${term.key}`,
+ title,
+ image,
+ content,
+ };
+ });
+ }
+
+ override transform(result: SearchResult): SearchResultTerm {
+ const document = this.documents[result.id];
+ const termsByField = getTermsByField(result.match);
+ return {
+ id: document.id,
+ type: document.type,
+ url: document.url,
+ title: highlightMatches(document.title, termsByField.title),
+ image: document.image,
+ content: highlightMatches(document.content, termsByField.content, true),
+ };
+ }
+}
+
export default defineEventHandler(async (event) => {
const indices = Object.fromEntries(
[
@@ -439,6 +511,7 @@ export default defineEventHandler(async (event) => {
new SearchIndexLink(),
new SearchIndexFaq(),
new SearchIndexBlog(),
+ new SearchIndexTerm(),
].map((index) => [index.TYPE, index]),
);
await Promise.all(Object.values(indices).map((index) => index.init(config)));
diff --git a/server/express/terms.ts b/server/express/terms.ts
index ac920ea6b..7d9d22591 100644
--- a/server/express/terms.ts
+++ b/server/express/terms.ts
@@ -71,7 +71,7 @@ const linkOtherVersions = async (db: Database, isGranted: Request['isGranted'],
const router = Router();
-const getTermsEntries = defineCachedFunction(async (db: Database, isGranted: Request['isGranted']) => {
+export const getTermsEntries = defineCachedFunction(async (db: Database, isGranted: Request['isGranted']) => {
return await linkOtherVersions(
db,
isGranted,