返回 DeepSeek-Reasonix
WorkspaceTreeMenu.tsx
根目录 / desktop / frontend / src / components / WorkspaceTreeMenu.tsx
1 import { useEffect, useState } from "react";
2 import { ExternalLink, FileText, FolderOpen, MessageSquarePlus, Pin, PinOff, TerminalSquare } from "lucide-react";
3 import { app } from "../lib/bridge";
4 import { useT } from "../lib/i18n";
5 import { useToast } from "../lib/toast";
6 import {
7 WORKSPACE_CONTEXT_MENU_FILE_HEIGHT,
8 WORKSPACE_CONTEXT_MENU_REF_HEIGHT,
9 workspacePathCopyMenuItems,
10 } from "../lib/workspacePathCopyMenuItems";
11 import { FloatingMenu, FloatingMenuItems } from "./FloatingMenu";
12
13 export interface WorkspaceTreeMenuTarget {
14 x: number;
15 y: number;
16 path: string;
17 isDir: boolean;
18 }
19
20 export function WorkspaceTreeMenu({
21 target,
22 workspaceTabId,
23 isScopeCurrent,
24 onClose,
25 onOpenInTerminal,
26 onAddReference,
27 onAddFile,
28 }: {
29 target: WorkspaceTreeMenuTarget;
30 workspaceTabId: string;
31 isScopeCurrent: () => boolean;
32 onClose: () => void;
33 onOpenInTerminal?: (path: string) => void;
34 onAddReference: () => void;
35 onAddFile: () => void;
36 }) {
37 const t = useT();
38 const { showToast } = useToast();
39 const [isPinned, setIsPinned] = useState(false);
40
41 useEffect(() => {
42 if (target.isDir || !workspaceTabId) return;
43 let active = true;
44 app.GetPinnedFilesForTab(workspaceTabId)
45 .then((files) => {
46 if (!active || !Array.isArray(files)) return;
47 const normalizedTarget = target.path.replace(/^[/\\]+/, "").replace(/\\/g, "/");
48 const found = files.some(
49 (f) => f.path.replace(/^[/\\]+/, "").replace(/\\/g, "/") === normalizedTarget
50 );
51 setIsPinned(found);
52 })
53 .catch(() => {});
54 return () => {
55 active = false;
56 };
57 }, [workspaceTabId, target.path, target.isDir]);
58
59 const closeThen = (action: () => void) => {
60 onClose();
61 action();
62 };
63
64 return (
65 <FloatingMenu
66 x={target.x}
67 y={target.y}
68 estimatedHeight={target.isDir ? WORKSPACE_CONTEXT_MENU_REF_HEIGHT : WORKSPACE_CONTEXT_MENU_FILE_HEIGHT + 32}
69 className="workspace-tree-menu"
70 >
71 <FloatingMenuItems
72 items={[
73 ...(target.isDir
74 ? []
75 : [{
76 icon: <ExternalLink size={14} />,
77 label: t("workspace.openWithDefaultApp"),
78 onSelect: () => closeThen(() => void app.OpenWorkspacePathForTab(workspaceTabId, target.path).catch(() => {})),
79 }]),
80 {
81 icon: <FolderOpen size={14} />,
82 label: t("workspace.revealInFileManager"),
83 onSelect: () => closeThen(() => void app.RevealWorkspacePathForTab(workspaceTabId, target.path).catch(() => {})),
84 },
85 ...(onOpenInTerminal
86 ? [{
87 icon: <TerminalSquare size={14} />,
88 label: t("workspace.openInTerminal"),
89 onSelect: () => closeThen(() => onOpenInTerminal(target.path)),
90 }]
91 : []),
92 ...workspacePathCopyMenuItems({
93 path: target.path,
94 resolveAbsolutePath: () => app.ResolveWorkspacePathForTab(workspaceTabId, target.path),
95 isScopeCurrent,
96 close: onClose,
97 relativeLabel: t("workspace.copyRelativePath"),
98 absoluteLabel: t("workspace.copyAbsolutePath"),
99 }),
100 { separator: true },
101 {
102 icon: <MessageSquarePlus size={14} />,
103 label: target.isDir ? t("workspace.addFolderReferenceToChat") : t("workspace.addFileReferenceToChat"),
104 onSelect: onAddReference,
105 },
106 ...(target.isDir
107 ? []
108 : [
109 {
110 icon: <FileText size={14} />,
111 label: t("workspace.addFileContentToChat"),
112 onSelect: onAddFile,
113 },
114 {
115 icon: isPinned ? <PinOff size={14} /> : <Pin size={14} />,
116 label: isPinned ? t("workspace.unpinFileFromContext") : t("workspace.pinFileToContext"),
117 onSelect: () =>
118 closeThen(() => {
119 if (isPinned) {
120 void app.UnpinFileForTab(workspaceTabId, target.path).catch((err: unknown) => {
121 showToast(String((err as Error)?.message || err), "error");
122 });
123 } else {
124 void app.PinFileForTab(workspaceTabId, target.path).catch((err: unknown) => {
125 showToast(String((err as Error)?.message || err), "error");
126 });
127 }
128 }),
129 },
130 ]),
131 ]}
132 />
133 </FloatingMenu>
134 );
135 }
136
136 lines Plain Text