spb/trouve-ka Public
Trouve-KA — moteur de recherche web indépendant, Québec-first. Crawler distribué, index OpenSearch, ranking bilingue, galerie d'images. En prod : www.trouve-ka.com
Python 76.8%
TypeScript 15.7%
SQL 3.9%
Shell 1.4%
CSS 1.3%
Dockerfile 0.7%
1/**2 * Trouve-KA — rendu sécurisé des extraits avec surlignage <em>3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 */67import { Fragment } from "react";8import { cn } from "@/lib/utils";910/** Retire toute balise HTML restante (le contenu distant est non fiable). */11function stripTags(value: string): string {12 return value.replace(/<[^>]*>/g, "");13}1415/** Décode les entités HTML courantes (l'ordre garde `&` en dernier). */16function decodeEntities(value: string): string {17 return value18 .replace(/</g, "<")19 .replace(/>/g, ">")20 .replace(/"/g, '"')21 .replace(/�?39;/g, "'")22 .replace(/ /g, " ")23 .replace(/&/g, "&");24}2526interface SnippetProps {27 html: string;28 className?: string;29}3031/**32 * Rend un extrait de recherche sans `dangerouslySetInnerHTML` : seuls les33 * segments `<em>` du backend deviennent des éléments de surlignage; tout le34 * reste est traité comme du texte brut (React échappe automatiquement).35 */36export function Snippet({ html, className }: SnippetProps) {37 const parts = html.split(/<em>([\s\S]*?)<\/em>/g);38 return (39 <p className={cn("break-words", className)}>40 {parts.map((part, index) => {41 const text = decodeEntities(stripTags(part));42 if (!text) return null;43 return index % 2 === 1 ? (44 <em45 key={index}46 className="rounded-[3px] bg-lime-soft px-0.5 not-italic font-semibold text-ink"47 >48 {text}49 </em>50 ) : (51 <Fragment key={index}>{text}</Fragment>52 );53 })}54 </p>55 );56}57