1import { type ProviderDriverKind, type ProviderInstanceId } from "@t3tools/contracts";
2import { memo } from "react";
3import { CheckIcon, StarIcon } from "lucide-react";
4import {
5 getDisplayModelName,
6 getProviderRowLabel,
7 getTriggerDisplayModelLabel,
8 type ModelEsque,
9} from "./providerIconUtils";
10import { ComboboxItem } from "../ui/combobox";
11import { Button } from "../ui/button";
12import { Badge } from "../ui/badge";
13import { Kbd } from "../ui/kbd";
14import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
15import { cn } from "~/lib/utils";
16import { modelPickerModelKey } from "./modelPickerKeys";
17import { ProviderInstanceIcon } from "./ProviderInstanceIcon";
18
19export const ModelListRow = memo(function ModelListRow(props: {
20 index: number;
21 model: ModelEsque;
22 /** Instance the model belongs to — the routing key used in combobox values. */
23 instanceId: ProviderInstanceId;
24 /** Driver kind of the instance — used for the provider icon glyph. */
25 driverKind: ProviderDriverKind;
26 /**
27 * Display name to show in the secondary line (provider footer). Usually
28 * the instance's configured `displayName` so custom instances like
29 * "Codex Personal" render with their user-authored label.
30 */
31 providerDisplayName: string;
32 providerAccentColor?: string | undefined;
33 acpRegistryAgentId?: string | undefined;
34 acpRegistryIconUrl?: string | undefined;
35 isFavorite: boolean;
36 isSelected: boolean;
37 showSelection?: boolean;
38 showProvider: boolean;
39 preferShortName?: boolean;
40 useTriggerLabel?: boolean;
41 showNewBadge?: boolean;
42 unavailable?: boolean;
43 jumpLabel?: string | null;
44 disabledReason?: string | null;
45 onToggleFavorite: () => void;
46}) {
47 const providerLabel = getProviderRowLabel(props.providerDisplayName, props.model.subProvider);
48
49 const row = (
50 <ComboboxItem
51 hideIndicator
52 index={props.index}
53 value={modelPickerModelKey(props.instanceId, props.model.slug)}
54 disabled={Boolean(props.disabledReason)}
55 className={cn(
56 "group relative w-full !min-w-0 max-w-full cursor-pointer",
57 props.disabledReason &&
58 "data-disabled:pointer-events-auto data-disabled:cursor-not-allowed",
59 )}
60 >
61 <div className="min-w-0 flex-1 text-left">
62 <div className="flex min-w-0 items-center gap-2">
63 <div className="min-w-0 truncate text-xs font-medium leading-snug">
64 {props.useTriggerLabel
65 ? getTriggerDisplayModelLabel(props.model)
66 : getDisplayModelName(
67 props.model,
68 props.preferShortName ? { preferShortName: true } : undefined,
69 )}
70 </div>
71 {props.showNewBadge ? (
72 <span
73 className="shrink-0 rounded border border-update/35 bg-update/15 px-0.5 py-px text-3xs font-bold uppercase leading-none tracking-wide text-update-foreground"
74 aria-label="New model"
75 >
76 New
77 </span>
78 ) : null}
79 {props.unavailable ? (
80 <Badge variant="outline" size="sm">
81 Unavailable
82 </Badge>
83 ) : null}
84 </div>
85 {props.showProvider && (
86 <div className="mt-1 flex items-center gap-1.5">
87 <ProviderInstanceIcon
88 driverKind={props.driverKind}
89 displayName={props.providerDisplayName}
90 acpRegistryAgentId={props.acpRegistryAgentId}
91 acpRegistryIconUrl={props.acpRegistryIconUrl}
92 className="size-3"
93 iconClassName="size-3"
94 />
95 <span className="truncate text-xs font-normal leading-snug text-muted-foreground/70">
96 {providerLabel}
97 </span>
98 </div>
99 )}
100 </div>
101
102 <div className="flex shrink-0 items-center gap-1.5">
103 {props.showSelection && props.isSelected ? (
104 <CheckIcon className="size-3.5" aria-hidden="true" />
105 ) : null}
106 {props.jumpLabel ? <Kbd>{props.jumpLabel}</Kbd> : null}
107 <Tooltip>
108 <TooltipTrigger
109 render={
110 <Button
111 size="icon-xs"
112 variant="ghost-muted"
113 className="-mr-1 shrink-0"
114 onClick={(event) => {
115 event.stopPropagation();
116 props.onToggleFavorite();
117 }}
118 onKeyDown={(event) => {
119 event.stopPropagation();
120 }}
121 disabled={Boolean(props.disabledReason)}
122 aria-label={props.isFavorite ? "Remove from favorites" : "Add to favorites"}
123 >
124 <StarIcon
125 className={cn(
126 "size-3.5 sm:size-3",
127 props.isFavorite && "fill-current text-warning",
128 )}
129 />
130 </Button>
131 }
132 />
133 <TooltipPopup side="top" align="center">
134 {props.isFavorite ? "Remove from favorites" : "Add to favorites"}
135 </TooltipPopup>
136 </Tooltip>
137 </div>
138 </ComboboxItem>
139 );
140
141 if (!props.disabledReason) {
142 return row;
143 }
144
145 return (
146 <Tooltip>
147 <TooltipTrigger render={row} />
148 <TooltipPopup side="left" align="center">
149 {props.disabledReason}
150 </TooltipPopup>
151 </Tooltip>
152 );
153});