search: slightly better results appearance

This commit is contained in:
2026-02-14 00:33:15 -08:00
parent acca7be7f0
commit e3829e813f
5 changed files with 180 additions and 11 deletions

View File

@@ -1,10 +1,25 @@
import { Search } from "lucide-preact";
import type { SearchDetail, SearchResultItem } from "@/lib/api";
import { MarkdownContent } from "@/components/markdown/markdown-content";
function cleanResultText(input: string) {
return input
.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, "$1")
.replace(/\[\s*\]/g, " ")
.replace(/(^|\s)#{1,6}\s*/g, "$1")
.replace(/^\s*[-*+]\s+/gm, "")
.replace(/(\*\*|__|\*|_|`{1,3}|~~)/g, "")
.replace(/\r?\n+/g, " ")
.replace(/\s{2,}/g, " ")
.trim();
}
function summarizeResult(result: SearchResultItem) {
const highlights = Array.isArray(result.highlights) ? result.highlights.filter(Boolean) : [];
if (highlights.length) return highlights.join(" ").slice(0, 420);
return (result.text ?? "").slice(0, 420);
const raw = highlights.length ? highlights.join(" ") : result.text ?? "";
const cleaned = cleanResultText(raw);
if (cleaned.length <= 680) return cleaned;
return `${cleaned.slice(0, 679).trimEnd()}`;
}
function formatHost(url: string) {
@@ -15,6 +30,17 @@ function formatHost(url: string) {
}
}
function normalizeHref(href: string) {
try {
const parsed = new URL(href);
parsed.hash = "";
const normalized = parsed.toString();
return normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
} catch {
return href.trim().replace(/\/$/, "");
}
}
type Props = {
search: SearchDetail | null;
isLoading: boolean;
@@ -24,6 +50,24 @@ type Props = {
};
export function SearchResultsPanel({ search, isLoading, isRunning, showPrompt = true, className }: Props) {
const citationEntries = (search?.answerCitations ?? [])
.map((citation, index) => {
const href = citation.url || citation.id || "";
if (!href) return null;
return {
href,
normalizedHref: normalizeHref(href),
index: index + 1,
label: citation.title?.trim() || formatHost(href),
};
})
.filter((entry): entry is { href: string; normalizedHref: string; index: number; label: string } => !!entry);
const resolveCitationIndex = (href: string) => {
const normalized = normalizeHref(href);
return citationEntries.find((entry) => entry.normalizedHref === normalized)?.index;
};
return (
<div className={className ?? "mx-auto w-full max-w-4xl"}>
{search?.query ? (
@@ -41,22 +85,28 @@ export function SearchResultsPanel({ search, isLoading, isRunning, showPrompt =
<section className="mb-6 rounded-xl border border-slate-600/60 bg-[#121a2e] p-4">
<p className="text-xs font-semibold uppercase tracking-wide text-sky-300/90">Answer</p>
{isRunning && !search?.answerText ? <p className="mt-2 text-sm text-muted-foreground">Generating answer...</p> : null}
{search?.answerText ? <p className="mt-2 whitespace-pre-wrap text-sm leading-6 text-slate-100">{search.answerText}</p> : null}
{search?.answerText ? (
<MarkdownContent
markdown={search.answerText}
mode="citationTokens"
resolveCitationIndex={resolveCitationIndex}
className="mt-2 text-sm leading-6 text-slate-100"
/>
) : null}
{search?.answerError ? <p className="mt-2 text-sm text-red-500">{search.answerError}</p> : null}
{!!search?.answerCitations?.length && (
{!!citationEntries.length && (
<div className="mt-3 flex flex-wrap gap-2">
{search.answerCitations.slice(0, 6).map((citation, index) => {
const href = citation.url || citation.id || "";
if (!href) return null;
{citationEntries.slice(0, 8).map((citation) => {
return (
<a
key={`${href}-${index}`}
href={href}
key={`${citation.href}-${citation.index}`}
href={citation.href}
target="_blank"
rel="noreferrer"
className="rounded-md border border-slate-500/60 px-2 py-1 text-xs text-sky-200 hover:bg-slate-700/40"
>
{citation.title?.trim() || formatHost(href)}
<span className="mr-1 rounded bg-slate-700/80 px-1 py-0.5 text-[10px] text-slate-100">{citation.index}</span>
{citation.label}
</a>
);
})}
@@ -93,7 +143,7 @@ export function SearchResultsPanel({ search, isLoading, isRunning, showPrompt =
{(result.publishedDate || result.author) && (
<p className="mt-1 text-xs text-muted-foreground">{[result.publishedDate, result.author].filter(Boolean).join(" • ")}</p>
)}
{summary ? <p className="mt-2 whitespace-pre-wrap text-sm leading-6 text-slate-200">{summary}</p> : null}
{summary ? <p className="mt-2 text-sm leading-6 text-slate-200">{summary}</p> : null}
</article>
);
})}