feat: hover rename/delete icons in treeview, resizable tree and sidebar panels

This commit is contained in:
Agent Zero
2026-07-17 22:24:26 +02:00
parent 3fc4dcdb96
commit 4f23c60fd4
3 changed files with 57 additions and 25 deletions
@@ -52,6 +52,7 @@ function TreeItem({
onContextMenu: (e: React.MouseEvent, type: 'session' | 'folder' | 'root', id: string | null) => void;
onDragSession: (sessionId: string) => void;
onDropToFolder: (folderId: string | null) => void;
onDelete: (id: string, type: 'session' | 'folder') => void;
}) {
const [expanded, setExpanded] = useState(true);
const [isDragOver, setIsDragOver] = useState(false);
@@ -94,6 +95,12 @@ function TreeItem({
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
</svg>
<span className="flex-1 truncate">{node.folder!.name}</span>
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e.nativeEvent, 'folder', node.folder!.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg>
</button>
<button onClick={(e) => { e.stopPropagation(); onDelete(node.folder!.id, 'folder'); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600 p-0.5" title="Löschen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
</button>
</div>
)}
@@ -124,6 +131,12 @@ function TreeItem({
<span className={clsx('flex-1 truncate', activeSessionId === session.id && 'font-bold text-primary-700')}>
{session.title}
</span>
<button onClick={(e) => { e.stopPropagation(); onContextMenu(e.nativeEvent, 'session', session.id); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-primary-600 p-0.5" title="Umbenennen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" /></svg>
</button>
<button onClick={(e) => { e.stopPropagation(); onDelete(session.id, 'session'); }} className="opacity-0 group-hover:opacity-100 text-secondary-400 hover:text-red-600 p-0.5" title="Löschen">
<svg className="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /></svg>
</button>
</div>
))}
</div>
@@ -154,6 +167,7 @@ function TreeItem({
onContextMenu={onContextMenu}
onDragSession={onDragSession}
onDropToFolder={onDropToFolder}
onDelete={onDelete}
/>
))}
</div>
@@ -313,6 +327,7 @@ export function SessionList({ activeSessionId, onSelectSession, isSidebar, class
onContextMenu={handleContextMenu}
onDragSession={setDragSessionId}
onDropToFolder={handleDropToFolder}
onDelete={handleDelete}
/>
))}
</div>
+10 -2
View File
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { ChatWindow } from '@/components/ai/ChatWindow';
import { ResizablePanel } from '@/components/ui/ResizablePanel';
import { createSession, fetchSessions } from '@/api/ai';
import { useUIStore } from '@/store/uiStore';
@@ -9,7 +10,6 @@ export function AISidebar() {
const [loading, setLoading] = useState(true);
useEffect(() => {
// Find or create a sidebar session
(async () => {
try {
const sessions = await fetchSessions(true);
@@ -28,7 +28,14 @@ export function AISidebar() {
}, []);
return (
<div className="w-80 flex-shrink-0 border-l border-secondary-200 bg-white flex flex-col h-full">
<ResizablePanel
initialWidth={320}
minWidth={240}
maxWidth={600}
className="border-l border-secondary-200 bg-white"
data-testid="ai-sidebar-pane"
>
<div className="h-full flex flex-col">
<div className="h-12 flex items-center justify-between px-3 border-b border-secondary-200">
<span className="text-sm font-medium text-secondary-700">KI Assistent</span>
<button
@@ -51,5 +58,6 @@ export function AISidebar() {
)}
</div>
</div>
</ResizablePanel>
);
}
+11 -2
View File
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { SessionList } from '@/components/ai/SessionList';
import { ChatWindow } from '@/components/ai/ChatWindow';
import { ResizablePanel } from '@/components/ui/ResizablePanel';
import { usePluginToolbarStore } from '@/store/pluginToolbarStore';
import { fetchAgents, type AIAgent } from '@/api/ai';
@@ -69,8 +70,16 @@ export function AIAssistantPage() {
return (
<div className="flex h-full" data-testid="ai-assistant-page">
{/* Desktop: two-pane layout */}
<div className="hidden md:flex w-72 flex-shrink-0 border-r border-secondary-200 bg-white flex-col">
<SessionList key={refreshKey} activeSessionId={activeSessionId} onSelectSession={setActiveSessionId} className="flex-1" />
<div className="hidden md:flex">
<ResizablePanel
initialWidth={288}
minWidth={200}
maxWidth={500}
className="border-r border-secondary-200 bg-white"
data-testid="ai-session-pane"
>
<SessionList key={refreshKey} activeSessionId={activeSessionId} onSelectSession={setActiveSessionId} className="h-full" />
</ResizablePanel>
</div>
<div className="hidden md:flex flex-1 flex-col">
{activeSessionId ? <ChatWindow sessionId={activeSessionId} agentId={selectedAgentId} className="flex-1" /> : (