返回 DeepSeek-Reasonix
ComposerContextCard.tsx
根目录 / desktop / frontend / src / components / ComposerContextCard.tsx
1 import type { ReactNode } from "react";
2 import { FileText, Folder, MessageSquare, X } from "lucide-react";
3 import { Tooltip } from "./Tooltip";
4
5 type ComposerContextCardVariant = "attachment" | "workspace" | "session" | "selection";
6
7 export function ComposerContextCard({
8 variant,
9 tooltipLabel,
10 removeLabel,
11 onRemove,
12 removeDisabled = false,
13 removeIconSize,
14 previewUrl,
15 onImageClick,
16 imageOnly = false,
17 folder = false,
18 name,
19 meta,
20 label,
21 icon,
22 }: {
23 variant: ComposerContextCardVariant;
24 tooltipLabel: ReactNode;
25 removeLabel: string;
26 onRemove: () => void;
27 removeDisabled?: boolean;
28 removeIconSize?: number;
29 previewUrl?: string;
30 /** called when the image thumbnail is clicked (only for image attachments with previewUrl) */
31 onImageClick?: () => void;
32 imageOnly?: boolean;
33 folder?: boolean;
34 name?: string;
35 meta?: string;
36 label?: ReactNode;
37 icon?: ReactNode;
38 }) {
39 const variantClass = previewUrl
40 ? "composer-context__item--image"
41 : variant === "workspace"
42 ? `composer-context__item--workspace composer-context__item--${folder ? "folder" : "file"}`
43 : variant === "session"
44 ? "composer-context__item--session"
45 : variant === "selection"
46 ? "composer-context__item--selection composer-context__item--attachment"
47 : "composer-context__item--attachment";
48 const iconNode = icon ?? (variant === "session" ? <MessageSquare size={15} /> : folder ? <Folder size={15} /> : <FileText size={15} />);
49 return (
50 <div className={`composer-context__item ${variantClass}${imageOnly ? " composer-context__item--image-only" : ""}`}>
51 <Tooltip label={tooltipLabel}>
52 <span className="composer-context__label">
53 {previewUrl ? (
54 <span
55 className={`composer-context__thumb${onImageClick ? " composer-context__thumb--interactive" : ""}`}
56 onClick={onImageClick}
57 role={onImageClick ? "button" : undefined}
58 tabIndex={onImageClick ? 0 : undefined}
59 onKeyDown={onImageClick ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onImageClick(); } } : undefined}
60 >
61 <img src={previewUrl} alt="" draggable={false} />
62 </span>
63 ) : variant === "attachment" || variant === "selection" ? (
64 <>
65 <span className="composer-context__fileicon">
66 {icon ?? <FileText size={20} />}
67 </span>
68 <span className="composer-context__main">
69 <span className="composer-context__name">{name}</span>
70 {meta && <span className="composer-context__meta">{meta}</span>}
71 </span>
72 </>
73 ) : (
74 <>
75 {iconNode}
76 <span>{label}</span>
77 </>
78 )}
79 </span>
80 </Tooltip>
81 <Tooltip label={removeLabel} className="composer-context__remove-trigger">
82 <button
83 className="composer-context__remove"
84 type="button"
85 aria-label={removeLabel}
86 disabled={removeDisabled}
87 onClick={onRemove}
88 >
89 <X size={removeIconSize ?? (variant === "attachment" ? 14 : 13)} aria-hidden="true" />
90 </button>
91 </Tooltip>
92 </div>
93 );
94 }
95
95 lines Plain Text