SPB Git

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%
2.5 KB · 73 lines tsx
Raw Blame History
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