summaryrefslogtreecommitdiff
path: root/frontend/src/App.tsx
diff options
context:
space:
mode:
authorblackhao <13851610112@163.com>2025-12-05 21:02:12 -0600
committerblackhao <13851610112@163.com>2025-12-05 21:02:12 -0600
commitbcb44d5a7c4b17afd7ba64be5b497d74afc69fb6 (patch)
tree0ec4aa945e6f55fd68a825e19a58e170c64c5e8b /frontend/src/App.tsx
parentd9868550e66fe8aaa7fff55a8e24b871ee51e3b1 (diff)
right click logicHEADmain
Diffstat (limited to 'frontend/src/App.tsx')
-rw-r--r--frontend/src/App.tsx118
1 files changed, 77 insertions, 41 deletions
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 1eaafec..8c52751 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,16 +1,19 @@
-import { useCallback, useRef } from 'react';
+import { useCallback, useRef, useState } from 'react';
import ReactFlow, {
Background,
Controls,
MiniMap,
ReactFlowProvider,
Panel,
- useReactFlow
+ useReactFlow,
+ type Node,
+ type Edge
} from 'reactflow';
import 'reactflow/dist/style.css';
import useFlowStore from './store/flowStore';
import LLMNode from './components/nodes/LLMNode';
import Sidebar from './components/Sidebar';
+import { ContextMenu } from './components/ContextMenu';
import { Plus } from 'lucide-react';
const nodeTypes = {
@@ -25,59 +28,44 @@ function Flow() {
onEdgesChange,
onConnect,
addNode,
+ deleteEdge,
+ deleteNode,
+ deleteBranch,
setSelectedNode
} = useFlowStore();
const reactFlowWrapper = useRef<HTMLDivElement>(null);
const { project } = useReactFlow();
+ const [menu, setMenu] = useState<{ x: number; y: number; type: 'pane' | 'node' | 'edge'; id?: string } | null>(null);
- const handleAddNode = () => {
- const id = `node_${Date.now()}`;
- addNode({
- id,
- type: 'llmNode',
- position: { x: Math.random() * 400, y: Math.random() * 400 },
- data: {
- label: 'New Question',
- model: 'gpt-4o',
- temperature: 0.7,
- systemPrompt: '',
- userPrompt: '',
- mergeStrategy: 'smart',
- messages: [],
- traces: [],
- outgoingTraces: [],
- forkedTraces: [],
- response: '',
- status: 'idle',
- inputs: 1
- },
- });
+ const onPaneClick = () => {
+ setSelectedNode(null);
+ setMenu(null);
};
- const onNodeClick = (_: any, node: any) => {
- setSelectedNode(node.id);
+ const handlePaneContextMenu = (event: React.MouseEvent) => {
+ event.preventDefault();
+ setMenu({ x: event.clientX, y: event.clientY, type: 'pane' });
};
- const onPaneClick = () => {
- setSelectedNode(null);
+ const handleNodeContextMenu = (event: React.MouseEvent, node: Node) => {
+ event.preventDefault();
+ setMenu({ x: event.clientX, y: event.clientY, type: 'node', id: node.id });
};
- const onPaneContextMenu = (event: React.MouseEvent) => {
+ const handleEdgeContextMenu = (event: React.MouseEvent, edge: Edge) => {
event.preventDefault();
- const bounds = reactFlowWrapper.current?.getBoundingClientRect();
- if (!bounds) return;
-
- const position = project({
- x: event.clientX - bounds.left,
- y: event.clientY - bounds.top
- });
+ setMenu({ x: event.clientX, y: event.clientY, type: 'edge', id: edge.id });
+ };
- const id = `node_${Date.now()}`;
- addNode({
+ const handleAddNode = (position?: { x: number, y: number }) => {
+ const id = `node_${Date.now()}`;
+ const pos = position || { x: Math.random() * 400, y: Math.random() * 400 };
+
+ addNode({
id,
type: 'llmNode',
- position,
+ position: pos,
data: {
label: 'New Question',
model: 'gpt-4o',
@@ -94,6 +82,11 @@ function Flow() {
inputs: 1
},
});
+ setMenu(null);
+ };
+
+ const onNodeClick = (_: any, node: Node) => {
+ setSelectedNode(node.id);
};
return (
@@ -108,7 +101,9 @@ function Flow() {
nodeTypes={nodeTypes}
onNodeClick={onNodeClick}
onPaneClick={onPaneClick}
- onPaneContextMenu={onPaneContextMenu} // Use Right Click to add node for now
+ onPaneContextMenu={handlePaneContextMenu}
+ onNodeContextMenu={handleNodeContextMenu}
+ onEdgeContextMenu={handleEdgeContextMenu}
fitView
>
<Background color="#aaa" gap={16} />
@@ -116,13 +111,54 @@ function Flow() {
<MiniMap />
<Panel position="top-left">
<button
- onClick={handleAddNode}
+ onClick={() => handleAddNode()}
className="bg-white px-4 py-2 rounded-md shadow-md font-medium text-gray-700 hover:bg-gray-50 flex items-center gap-2"
>
<Plus size={16} /> Add Block
</button>
</Panel>
</ReactFlow>
+
+ {menu && (
+ <ContextMenu
+ x={menu.x}
+ y={menu.y}
+ onClose={() => setMenu(null)}
+ items={
+ menu.type === 'pane' ? [
+ {
+ label: 'Add Node',
+ onClick: () => {
+ const bounds = reactFlowWrapper.current?.getBoundingClientRect();
+ if (bounds) {
+ const position = project({
+ x: menu.x - bounds.left,
+ y: menu.y - bounds.top
+ });
+ handleAddNode(position);
+ }
+ }
+ }
+ ] : menu.type === 'node' ? [
+ {
+ label: 'Delete Node (Cascade)',
+ danger: true,
+ onClick: () => menu.id && deleteBranch(menu.id)
+ }
+ ] : [
+ {
+ label: 'Disconnect',
+ onClick: () => menu.id && deleteEdge(menu.id)
+ },
+ {
+ label: 'Delete Branch',
+ danger: true,
+ onClick: () => menu.id && deleteBranch(undefined, menu.id)
+ }
+ ]
+ }
+ />
+ )}
</div>
<Sidebar />
</div>