Skip to content

Commit e000705

Browse files
committed
refactor(ui): Bind row prefetch to nav links, not whole card
Warming on the entire card produced false positives: the card also holds the non-navigating Copy button and readable metadata a user may just be scanning, so hovering to read or to copy wrongly warmed a detail fetch. Hovering an actual link is a far stronger intent-to-navigate signal (instant.page's own model binds to anchors, not containers). Move the handlers onto the two affordances that actually lead to the detail route -- the title/id link and the Details button -- rather than only the button, since both are equal navigation paths. They share one warm via the deduped cache key.
1 parent e1181ca commit e000705

1 file changed

Lines changed: 9 additions & 5 deletions

File tree

frontend/src/Dashboard.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,22 @@ export function Dashboard() {
128128
/** A single row in the snippet list. */
129129
function SnippetRow({ snippet }: { snippet: SnippetSummary }) {
130130
// Warm the detail view on hover intent so clicking through feels instant.
131+
// Bind to the navigation affordances themselves (the title/id link and the
132+
// Details button) rather than the whole card: hovering an actual link is a
133+
// genuine intent-to-navigate signal, whereas the card also holds the
134+
// non-navigating Copy button and readable metadata a user may just be
135+
// scanning. Both links target the same detail route and share one warm.
131136
const prefetch = useHoverPrefetch(prefetchKey.snippetDetail(snippet.id), () =>
132137
api.getSnippet(snippet.id),
133138
);
134139
return (
135140
<li>
136-
<Card
137-
{...prefetch}
138-
className="flex flex-col gap-3 p-4 transition-colors hover:border-wisteria/40 sm:flex-row sm:items-center sm:justify-between sm:gap-4 md:p-5 lg:gap-6 lg:p-6"
139-
>
141+
<Card className="flex flex-col gap-3 p-4 transition-colors hover:border-wisteria/40 sm:flex-row sm:items-center sm:justify-between sm:gap-4 md:p-5 lg:gap-6 lg:p-6">
140142
<div className="min-w-0">
141143
{snippet.title ? (
142144
<>
143145
<Link
146+
{...prefetch}
144147
to={`/s/${snippet.id}`}
145148
className="block truncate text-sm font-medium text-wisteria-deep hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-wisteria/50"
146149
>
@@ -152,6 +155,7 @@ function SnippetRow({ snippet }: { snippet: SnippetSummary }) {
152155
</>
153156
) : (
154157
<Link
158+
{...prefetch}
155159
to={`/s/${snippet.id}`}
156160
className="block truncate rounded font-mono text-sm text-wisteria-deep hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-wisteria/50"
157161
>
@@ -172,7 +176,7 @@ function SnippetRow({ snippet }: { snippet: SnippetSummary }) {
172176
</div>
173177
<div className="flex shrink-0 items-center gap-2">
174178
<CopyButton value={deliveryUrl(snippet.id)} label="Copy link" size="sm" />
175-
<Link to={`/s/${snippet.id}`}>
179+
<Link {...prefetch} to={`/s/${snippet.id}`}>
176180
<Button variant="secondary" size="sm">
177181
Details
178182
</Button>

0 commit comments

Comments
 (0)