返回 DeepSeek-Reasonix
ReadOnlyBatch.tsx
根目录 / desktop / frontend / src / components / ReadOnlyBatch.tsx
1 import { memo, useRef, useState } from "react";
2 import { ChevronRight } from "lucide-react";
3 import { useT } from "../lib/i18n";
4 import { useGSAPCollapse } from "../lib/useGSAPCollapse";
5 import type { Item } from "../lib/useController";
6 import { ToolCard } from "./ToolCard";
7
8 type ToolItem = Extract<Item, { kind: "tool" }>;
9
10 type ReadOnlyBatchProps = {
11 items: ToolItem[];
12 subcalls: ReadonlyMap<string, ToolItem[]>;
13 tabId?: string;
14 };
15
16 export const ReadOnlyBatch = memo(function ReadOnlyBatch({ items, subcalls, tabId }: ReadOnlyBatchProps) {
17 const t = useT();
18 const [open, setOpen] = useState(false);
19 const bodyRef = useRef<HTMLDivElement>(null);
20 useGSAPCollapse(bodyRef, open);
21
22 const readCount = items.filter((it) => it.name === "read_file" || it.name === "ls").length;
23 const searchCount = items.filter((it) => it.name === "grep" || it.name === "glob" || it.name === "web_fetch").length;
24
25 const parts: string[] = [];
26 if (readCount > 0) parts.push(t("tool.readCount", { n: readCount }));
27 if (searchCount > 0) parts.push(t("tool.searchCount", { n: searchCount }));
28 const otherCount = items.length - readCount - searchCount;
29 if (otherCount > 0) parts.push(t("tool.otherReadCount", { n: otherCount }));
30 const label = parts.join(" · ");
31
32 if (!label || items.length === 0) return null;
33
34 return (
35 <div className={`readonly-batch${open ? " readonly-batch--open" : ""}`} data-entrance={items[0]?.id}>
36 <button type="button" className="reasoning__head" onClick={() => setOpen((v) => !v)} aria-expanded={open}>
37 <ChevronRight className={`reasoning__chevron${open ? " reasoning__chevron--open" : ""}`} size={12} />
38 <span className="readonly-batch__label" data-creation-label={t("creation.toolCallsLabel")}>{label}</span>
39 </button>
40 <div ref={bodyRef} className="readonly-batch__body">
41 {items.map((it) => (
42 <ToolCard key={it.id} item={it} subcalls={subcalls.get(it.id)} tabId={tabId} />
43 ))}
44 </div>
45 </div>
46 );
47 });
48
48 lines Plain Text