Every abbreviation and technical term used in the site's copy (RBAC, NAPALM, config drift, ...) now links to a new /glossary page and shows a short definition on hover, wherever it appears in body text. Product and vendor brand names are deliberately excluded — the glossary stays a dictionary of vocabulary, not a company directory. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
34 lines
1.0 KiB
TypeScript
34 lines
1.0 KiB
TypeScript
import { Fragment } from 'react'
|
|
import GlossaryMark from '../components/GlossaryMark'
|
|
import { GLOSSARY } from '../glossary/terms'
|
|
|
|
function escapeRegExp(value: string): string {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
}
|
|
|
|
const MATCH_TO_ID = new Map<string, string>()
|
|
for (const entry of GLOSSARY) {
|
|
for (const surface of entry.match) {
|
|
MATCH_TO_ID.set(surface, entry.id)
|
|
}
|
|
}
|
|
|
|
const ALL_SURFACES = [...MATCH_TO_ID.keys()].sort((a, b) => b.length - a.length)
|
|
const GLOSSARY_REGEX = new RegExp(`\\b(${ALL_SURFACES.map(escapeRegExp).join('|')})\\b`, 'g')
|
|
|
|
/** Wraps every occurrence of a known glossary term in `text` with a hoverable, linked <GlossaryMark>. */
|
|
export function linkify(text: string): React.ReactNode {
|
|
const parts = text.split(GLOSSARY_REGEX)
|
|
if (parts.length === 1) return text
|
|
|
|
return parts.map((part, i) => {
|
|
const id = MATCH_TO_ID.get(part)
|
|
if (id === undefined) return <Fragment key={i}>{part}</Fragment>
|
|
return (
|
|
<GlossaryMark key={i} id={id}>
|
|
{part}
|
|
</GlossaryMark>
|
|
)
|
|
})
|
|
}
|