Skip to content

Commit ff8750f

Browse files
authored
Search results image summary (#604)
1 parent c89f20f commit ff8750f

File tree

7 files changed

+624
-25
lines changed

7 files changed

+624
-25
lines changed

app/components/navbar.tsx

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ type SearchSuggestion = {
142142
url: string
143143
segment: string
144144
title: string
145+
summary?: string
146+
imageUrl?: string
147+
imageAlt?: string
145148
}
146149

147150
function isPlainLeftClick(event: React.MouseEvent) {
@@ -527,14 +530,41 @@ function NavSearch({
527530
: 'hover:bg-secondary',
528531
)}
529532
>
530-
<div className="text-primary truncate text-base font-medium">
531-
{s.title}
532-
</div>
533-
<div className="text-secondary mt-1 flex items-baseline justify-between gap-3 text-sm">
534-
<span className="truncate">{s.segment}</span>
535-
<span className="shrink-0">
536-
{new URL(s.url).pathname}
537-
</span>
533+
<div className="flex items-start gap-3">
534+
<div className="shrink-0">
535+
{s.imageUrl ? (
536+
<img
537+
src={s.imageUrl}
538+
alt={s.imageAlt ?? ''}
539+
className="h-10 w-10 rounded-md object-cover"
540+
loading="lazy"
541+
/>
542+
) : (
543+
<div className="h-10 w-10 rounded-md bg-gray-200 dark:bg-gray-700" />
544+
)}
545+
</div>
546+
<div className="min-w-0 flex-1">
547+
<div className="text-primary truncate text-base font-medium">
548+
{s.title}
549+
</div>
550+
<div className="text-secondary mt-1 flex items-baseline justify-between gap-3 text-sm">
551+
<span className="truncate">{s.segment}</span>
552+
<span className="shrink-0">
553+
{(() => {
554+
try {
555+
return new URL(s.url).pathname
556+
} catch {
557+
return s.url
558+
}
559+
})()}
560+
</span>
561+
</div>
562+
{s.summary ? (
563+
<div className="text-secondary mt-2 line-clamp-2 text-sm">
564+
{s.summary}
565+
</div>
566+
) : null}
567+
</div>
538568
</div>
539569
</div>
540570
</li>

app/routes/mcp+/mcp.server.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ function createServer() {
263263
title: r.title ?? url ?? r.id,
264264
url: absoluteUrl,
265265
category: r.type ?? 'Results',
266+
summary: r.summary ?? r.snippet ?? null,
267+
imageUrl: r.imageUrl ?? null,
268+
imageAlt: r.imageAlt ?? null,
266269
})
267270
})
268271
.join('\n'),

app/routes/resources+/search.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ import {
55
semanticSearchKCD,
66
} from '#app/utils/semantic-search.server.ts'
77

8+
function normalizeSummary(value: unknown) {
9+
if (typeof value !== 'string') return undefined
10+
const text = value.replace(/\s+/g, ' ').trim()
11+
if (!text) return undefined
12+
// Keep payloads small for consumers like Discord embeds/autocomplete UIs.
13+
return text.length > 220 ? `${text.slice(0, 217)}...` : text
14+
}
15+
816
export async function loader({ request }: LoaderFunctionArgs) {
917
const query = new URL(request.url).searchParams.get('query')
1018
const domainUrl = getDomainUrl(request)
@@ -39,6 +47,9 @@ export async function loader({ request }: LoaderFunctionArgs) {
3947
url: absoluteUrl,
4048
segment: r.type ?? 'Results',
4149
title: r.title ?? url ?? r.id,
50+
summary: normalizeSummary(r.summary ?? r.snippet),
51+
imageUrl: r.imageUrl,
52+
imageAlt: r.imageAlt,
4253
}
4354
}),
4455
{ headers },

app/routes/search.tsx

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -181,28 +181,43 @@ function SearchResults({
181181
key={r.id}
182182
className="rounded-lg bg-gray-100 p-6 dark:bg-gray-800"
183183
>
184-
<div className="flex items-baseline justify-between gap-4">
185-
<div className="min-w-0">
186-
<H4 className="truncate">
187-
{href ? (
188-
<Link to={href}>{r.title ?? r.url ?? r.id}</Link>
184+
<div className="flex items-start justify-between gap-4">
185+
<div className="flex min-w-0 flex-1 items-start gap-4">
186+
<div className="shrink-0">
187+
{r.imageUrl ? (
188+
<img
189+
src={r.imageUrl}
190+
alt={r.imageAlt ?? ''}
191+
className="h-16 w-16 rounded-lg object-cover"
192+
loading="lazy"
193+
/>
189194
) : (
190-
r.id
195+
<div className="h-16 w-16 rounded-lg bg-gray-200 dark:bg-gray-700" />
191196
)}
192-
</H4>
193-
{r.type ? (
194-
<p className="text-sm text-slate-500">{r.type}</p>
195-
) : null}
197+
</div>
198+
<div className="min-w-0 flex-1">
199+
<H4 className="truncate">
200+
{href ? (
201+
<Link to={href}>{r.title ?? r.url ?? r.id}</Link>
202+
) : (
203+
r.id
204+
)}
205+
</H4>
206+
<div className="mt-1 flex flex-wrap items-baseline gap-x-3 gap-y-1 text-sm text-slate-500">
207+
{r.type ? <span>{r.type}</span> : null}
208+
{r.url ? <span className="truncate">{r.url}</span> : null}
209+
</div>
210+
{r.summary || r.snippet ? (
211+
<p className="mt-3 line-clamp-3 text-base text-slate-600 dark:text-slate-400">
212+
{r.summary ?? r.snippet}
213+
</p>
214+
) : null}
215+
</div>
196216
</div>
197217
<p className="shrink-0 text-sm text-slate-500">
198218
{Number.isFinite(r.score) ? r.score.toFixed(3) : ''}
199219
</p>
200220
</div>
201-
{r.snippet ? (
202-
<p className="mt-3 text-base text-slate-600 dark:text-slate-400">
203-
{r.snippet}
204-
</p>
205-
) : null}
206221
</li>
207222
)
208223
})}

0 commit comments

Comments
 (0)