feat(frontend): add assistant delete menu to prompt editor topbar

Place a more-actions dropdown beside the assistant ID with confirmed delete that returns to the assistant list after removal.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Xin Wang
2026-08-06 17:08:40 +08:00
parent b6e2ac5765
commit 723ee84925
2 changed files with 71 additions and 0 deletions

View File

@@ -18,8 +18,10 @@ import {
Database, Database,
Loader2, Loader2,
MessageSquareText, MessageSquareText,
MoreHorizontal,
Save, Save,
Sparkles, Sparkles,
Trash2,
Webhook, Webhook,
Wrench, Wrench,
} from "lucide-react"; } from "lucide-react";
@@ -45,6 +47,12 @@ import { SectionCard } from "@/components/editor/section-card";
import { VisionConfigSection } from "@/components/editor/vision-config-section"; import { VisionConfigSection } from "@/components/editor/vision-config-section";
import { TurnConfigEditor } from "@/components/turn-config-editor"; import { TurnConfigEditor } from "@/components/turn-config-editor";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { TopbarPortal } from "@/components/layout/topbar-portal"; import { TopbarPortal } from "@/components/layout/topbar-portal";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { import {
@@ -118,6 +126,7 @@ type PromptEditorProps = {
tools: Tool[]; tools: Tool[];
onBack: () => void; onBack: () => void;
onSave: () => void; onSave: () => void;
onDelete?: () => void | Promise<void>;
updateForm: <K extends keyof AssistantForm>( updateForm: <K extends keyof AssistantForm>(
key: K, key: K,
value: AssistantForm[K], value: AssistantForm[K],
@@ -142,6 +151,7 @@ export function PromptEditor({
tools, tools,
onBack, onBack,
onSave, onSave,
onDelete,
updateForm, updateForm,
handlePromptVisionEnabledChange, handlePromptVisionEnabledChange,
handlePromptModelChange, handlePromptModelChange,
@@ -171,6 +181,7 @@ export function PromptEditor({
const [activeSection, setActiveSection] = const [activeSection, setActiveSection] =
useState<PromptSectionId>("conversation"); useState<PromptSectionId>("conversation");
const [debugOpen, setDebugOpen] = useState(true); const [debugOpen, setDebugOpen] = useState(true);
const [deleting, setDeleting] = useState(false);
useEffect(() => { useEffect(() => {
const container = scrollContainerRef.current; const container = scrollContainerRef.current;
@@ -283,6 +294,16 @@ export function PromptEditor({
}); });
} }
async function handleDeleteSelect() {
if (!onDelete || deleting) return;
setDeleting(true);
try {
await onDelete();
} finally {
setDeleting(false);
}
}
return ( return (
<> <>
<TopbarPortal> <TopbarPortal>
@@ -294,6 +315,42 @@ export function PromptEditor({
onChange={(value) => updateForm("name", value)} onChange={(value) => updateForm("name", value)}
/> />
<AssistantIdentity assistantId={assistantId} /> <AssistantIdentity assistantId={assistantId} />
{onDelete && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon-sm"
className="border-hairline-strong text-muted-foreground hover:text-foreground"
disabled={!assistantId || deleting}
aria-label="更多操作"
>
{deleting ? (
<Loader2 size={15} className="animate-spin" />
) : (
<MoreHorizontal size={15} />
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
className="w-32 min-w-32 rounded-xl border border-hairline bg-popover p-1"
>
<DropdownMenuItem
variant="destructive"
className="rounded-lg"
disabled={deleting}
onSelect={(event) => {
event.preventDefault();
window.setTimeout(() => void handleDeleteSelect(), 0);
}}
>
<Trash2 size={14} />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</div> </div>
<div className="flex shrink-0 items-center gap-2"> <div className="flex shrink-0 items-center gap-2">

View File

@@ -639,6 +639,19 @@ export function AssistantPage(props: AssistantPageProps) {
); );
} }
async function handleDeleteCurrentAssistant() {
if (!editingId) return;
const assistantName = form.name.trim() || "未命名助手";
if (!window.confirm(`确认删除助手“${assistantName}”?`)) return;
setSaveError(null);
try {
await assistantsApi.remove(editingId);
router.push("/assistants");
} catch (error) {
setSaveError(error instanceof Error ? error.message : "删除失败");
}
}
// ---- 外部智能体平台Dify / FastGPT---- // ---- 外部智能体平台Dify / FastGPT----
function fillAgentPlatformForm(a: Assistant): AgentPlatformForm { function fillAgentPlatformForm(a: Assistant): AgentPlatformForm {
const next: AgentPlatformForm = { const next: AgentPlatformForm = {
@@ -1611,6 +1624,7 @@ export function AssistantPage(props: AssistantPageProps) {
tools={tools} tools={tools}
onBack={() => router.push("/assistants")} onBack={() => router.push("/assistants")}
onSave={() => void handleSavePrompt()} onSave={() => void handleSavePrompt()}
onDelete={() => void handleDeleteCurrentAssistant()}
updateForm={updateForm} updateForm={updateForm}
handlePromptVisionEnabledChange={handlePromptVisionEnabledChange} handlePromptVisionEnabledChange={handlePromptVisionEnabledChange}
handlePromptModelChange={handlePromptModelChange} handlePromptModelChange={handlePromptModelChange}