diff --git a/app/components/Article/Contents.tsx b/app/components/Article/Contents.tsx index 56ffe5f3..2b9c54c6 100644 --- a/app/components/Article/Contents.tsx +++ b/app/components/Article/Contents.tsx @@ -1,4 +1,5 @@ import {useRef, useEffect} from 'react' +import {questionUrl} from '~/routesMapper' import type {Glossary, PageId, GlossaryEntry} from '~/server-utils/stampy' const footnoteHTML = (el: HTMLDivElement, e: HTMLAnchorElement): string | null => { @@ -56,27 +57,19 @@ const updateTextNodes = (el: Node, textProcessor: (node: Node) => Node) => { * - use each glossary item only once */ const glossaryInjecter = (pageid: string, glossary: Glossary) => { - const unusedGlossaryEntries = Object.values(glossary) - .filter((item) => item.pageid != pageid) - .map(({term}) => term) - .sort((a, b) => b.length - a.length) - .map( - (term) => - [ - new RegExp(`(^|[^\\w-])(${term})($|[^\\w-])`, 'i'), - '$1$2$3', - ] as const - ) - - return (html: string) => { - return unusedGlossaryEntries.reduce((html, [match, replacement], index) => { - if (html.match(match)) { - unusedGlossaryEntries.splice(index, 1) - return html.replace(match, replacement) - } - return html - }, html) - } + const seen = new Set() + return (html: string) => + Object.values(glossary) + .filter((item) => item.pageid != pageid) + .sort((a, b) => b.alias.length - a.alias.length) + .reduce((html, {term, alias}) => { + const match = new RegExp(`(^|[^\\w-])(${alias})($|[^\\w-])`, 'i') + if (!seen.has(term) && html.search(match) >= 0) { + seen.add(term) + return html.replace(match, '$1$2$3') + } + return html + }, html) } const insertGlossary = (pageid: string, glossary: Glossary) => { @@ -101,6 +94,7 @@ const insertGlossary = (pageid: string, glossary: Glossary) => { */ const glossaryEntry = (e: Element) => { const entry = e.textContent && glossary[e?.textContent.toLowerCase().trim()] + console.log(e.textContent, entry) if ( // If the contents of this item aren't simply a glossary item word, then // something has gone wrong and the glossary-entry should be removed @@ -124,7 +118,7 @@ const insertGlossary = (pageid: string, glossary: Glossary) => { if (!entry) return undefined const link = entry.pageid && - `View full definition` + `View full definition` const image = entry.image && `` addPopup( e as HTMLSpanElement, diff --git a/app/routesMapper.ts b/app/routesMapper.ts index 5f65edea..79fe3063 100644 --- a/app/routesMapper.ts +++ b/app/routesMapper.ts @@ -1,5 +1,5 @@ export const questionUrl = ({pageid, title}: {pageid: string; title?: string}) => - `/questions/${pageid}/${title || ''}` + `/questions/${pageid}/${title?.replaceAll(' ', '-') || ''}` export const tagUrl = ({tagId, name}: {tagId?: number | string; name: string}) => tagId ? `/tags/${tagId}/${name}` : `/tags/${name}` diff --git a/app/server-utils/stampy.ts b/app/server-utils/stampy.ts index f2757dcf..75e1b0c8 100644 --- a/app/server-utils/stampy.ts +++ b/app/server-utils/stampy.ts @@ -47,6 +47,7 @@ export type Banner = { } export type GlossaryEntry = { term: string + alias: string pageid: PageId contents: string image: string @@ -328,13 +329,14 @@ export const loadGlossary = withCache('loadGlossary', async () => { const phrases = [values.phrase, ...values.aliases.split('\n')] const item = { pageid, + term: extractText(values.phrase), image: values.image?.url, contents: renderText(pageid, extractText(values.definition)), } return phrases .map((i) => extractText(i)) .filter(Boolean) - .map((phrase) => [phrase.toLowerCase(), {term: phrase, ...item}]) + .map((phrase) => [phrase.toLowerCase(), {alias: phrase, ...item}]) }) .flat() )