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 — galerie de résultats images3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 */67"use client";89import { useState } from "react";10import type { SearchResult } from "@/lib/types";1112function GalleryTile({ result }: { result: SearchResult }) {13 const [broken, setBroken] = useState(false);14 if (!result.image || broken) return null;1516 const domain = result.domain.replace(/^www\./, "");1718 return (19 <a20 href={result.url}21 target="_blank"22 rel="noopener noreferrer"23 className="group block rounded-card focus-visible:outline focus-visible:outline-2 focus-visible:outline-green"24 >25 <span className="block overflow-hidden rounded-card border-[1.5px] border-ink bg-surface shadow-[3px_3px_0_rgba(20,24,20,0.1)] transition-[transform,box-shadow] duration-150 group-hover:-translate-y-0.5 group-hover:shadow-[5px_5px_0_rgba(20,24,20,0.55)]">26 <img27 src={result.image}28 alt={result.title}29 loading="lazy"30 referrerPolicy="no-referrer"31 onError={() => setBroken(true)}32 className="aspect-[4/3] w-full bg-surface-2 object-cover"33 />34 </span>35 <span className="mt-2 block truncate text-[13px] font-medium leading-snug text-ink group-hover:underline group-hover:decoration-lime group-hover:decoration-2 group-hover:underline-offset-2">36 {result.title}37 </span>38 <span className="gk-mono block truncate text-[10.5px] font-bold uppercase tracking-[0.08em] text-green">39 {domain}40 </span>41 </a>42 );43}4445/** Grille de résultats en mode galerie (onglet Images). */46export function ImageGallery({ results }: { results: SearchResult[] }) {47 return (48 <div className="grid grid-cols-2 gap-x-4 gap-y-6 sm:grid-cols-3 md:grid-cols-4">49 {results.map((result) => (50 <GalleryTile key={result.url} result={result} />51 ))}52 </div>53 );54}5556/** Skeleton du mode galerie, aux mêmes dimensions que les tuiles. */57export function GallerySkeleton() {58 return (59 <div60 aria-hidden="true"61 className="grid animate-pulse grid-cols-2 gap-x-4 gap-y-6 sm:grid-cols-3 md:grid-cols-4"62 >63 {Array.from({ length: 12 }, (_, i) => (64 <div key={i}>65 <div className="aspect-[4/3] w-full rounded-card bg-[rgba(20,24,20,0.07)]" />66 <div className="mt-2 h-3.5 w-5/6 rounded bg-[rgba(20,24,20,0.07)]" />67 <div className="mt-1 h-3 w-1/2 rounded bg-[rgba(20,24,20,0.07)]" />68 </div>69 ))}70 </div>71 );72}73