1import type { ScopedThreadRef } from "@t3tools/contracts";
2import { X } from "lucide-react";
3
4import { isValidHistoryTimestamp, type BrowserHistoryEntry } from "~/browserHistoryStore";
5import { useNowMinute } from "~/hooks/useNowMinute";
6import { formatRelativeTimeLabel } from "~/timestampFormat";
7
8import { PreviewFaviconIcon } from "./PreviewFaviconIcon";
9
10interface Props {
11 threadRef: ScopedThreadRef;
12 entry: BrowserHistoryEntry;
13 onOpen: () => void;
14 onRemove: () => void;
15}
16
17export function PreviewRecentUrlCard({ threadRef, entry, onOpen, onRemove }: Props) {
18 const parsed = new URL(entry.url);
19 const path = parsed.pathname === "/" ? "" : parsed.pathname;
20 const label = `${parsed.host}${path}${parsed.search}${parsed.hash}`;
21 const visitedAt = isValidHistoryTimestamp(entry.lastVisitedAt)
22 ? formatRelativeTimeLabel(new Date(entry.lastVisitedAt).toISOString())
23 : "";
24 useNowMinute();
25 return (
26 <div className="group relative flex w-full items-center">
27 <button
28 type="button"
29 onClick={onOpen}
30 className="flex w-full items-center gap-3 px-3 py-3 pr-10 text-left hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
31 >
32 <PreviewFaviconIcon threadRef={threadRef} url={entry.url} />
33 <div className="flex min-w-0 flex-1 flex-col">
34 <span className="truncate text-sm font-medium text-foreground">
35 {entry.title ?? label}
36 </span>
37 <span className="truncate text-xs text-muted-foreground">
38 {entry.title ? `${label} · ` : ""}
39 {visitedAt}
40 </span>
41 </div>
42 </button>
43 <button
44 type="button"
45 aria-label={`Remove ${label} from history`}
46 onClick={onRemove}
47 className="absolute right-3 rounded p-1 text-muted-foreground opacity-0 hover:bg-accent hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring group-hover:opacity-100"
48 >
49 <X className="size-3.5" />
50 </button>
51 </div>
52 );
53}