1import type { ResolvedKeybindingsConfig } from "@t3tools/contracts";
2import { PinIcon, PinOffIcon } from "lucide-react";
3import type { KeyboardEvent } from "react";
4
5import { resolveShortcutCommand } from "../keybindings";
6
7import { cn } from "~/lib/utils";
8import { Button } from "./ui/button";
9
10/** The thread pin shortcut, which pins the highlighted row in a project list. */
11export function isProjectPinShortcut(
12 event: KeyboardEvent,
13 keybindings: ResolvedKeybindingsConfig,
14): boolean {
15 return (
16 resolveShortcutCommand(event.nativeEvent, keybindings, {
17 platform: navigator.platform,
18 context: { modelPickerOpen: false, editableFocus: true },
19 }) === "thread.pin"
20 );
21}
22
23/**
24 * Pin toggle for a project row in a menu or list. A pinned project always shows its pin;
25 * an unpinned one shows it only on the highlighted row. Keyboard users get a shortcut
26 * from the list that hosts it, so the button stays out of the tab order.
27 */
28export function ProjectPinButton(props: {
29 readonly name: string;
30 readonly pinned: boolean;
31 readonly onToggle: () => void;
32 readonly shortcutLabel?: string | null;
33}) {
34 const action = `${props.pinned ? "Unpin" : "Pin"} ${props.name}`;
35 return (
36 <span
37 className={cn(
38 "group/pin ml-auto flex",
39 !props.pinned && "opacity-0 in-data-highlighted:opacity-100 pointer-coarse:opacity-100",
40 )}
41 >
42 <Button
43 size="icon-xs"
44 variant="ghost-muted"
45 tabIndex={-1}
46 aria-hidden="true"
47 title={props.shortcutLabel ? `${action} (${props.shortcutLabel})` : action}
48 onPointerDown={(event) => event.stopPropagation()}
49 onMouseDown={(event) => event.preventDefault()}
50 onClick={(event) => {
51 event.preventDefault();
52 event.stopPropagation();
53 props.onToggle();
54 }}
55 >
56 {props.pinned ? (
57 <>
58 <PinIcon className="size-3.5 group-hover/pin:hidden" />
59 <PinOffIcon className="hidden size-3.5 group-hover/pin:block" />
60 </>
61 ) : (
62 <PinIcon className="size-3.5" />
63 )}
64 </Button>
65 </span>
66 );
67}