From 93dbe11014cf967690727c25e89d9d1075008c24 Mon Sep 17 00:00:00 2001
From: blackhao <13851610112@163.com>
Date: Sat, 6 Dec 2025 01:30:57 -0600
Subject: UX

---
 frontend/src/App.tsx | 115 ++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 96 insertions(+), 19 deletions(-)

(limited to 'frontend/src/App.tsx')

diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 8c52751..9ec1340 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -13,6 +13,7 @@ import 'reactflow/dist/style.css';
 import useFlowStore from './store/flowStore';
 import LLMNode from './components/nodes/LLMNode';
 import Sidebar from './components/Sidebar';
+import LeftSidebar from './components/LeftSidebar';
 import { ContextMenu } from './components/ContextMenu';
 import { Plus } from 'lucide-react';
 
@@ -31,12 +32,19 @@ function Flow() {
     deleteEdge,
     deleteNode,
     deleteBranch,
-    setSelectedNode 
+    setSelectedNode,
+    toggleNodeDisabled,
+    archiveNode,
+    createNodeFromArchive,
+    toggleTraceDisabled
   } = 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 [isLeftOpen, setIsLeftOpen] = useState(true);
+  const [isRightOpen, setIsRightOpen] = useState(true);
 
   const onPaneClick = () => {
     setSelectedNode(null);
@@ -73,6 +81,7 @@ function Flow() {
         systemPrompt: '',
         userPrompt: '',
         mergeStrategy: 'smart',
+        reasoningEffort: 'medium',  // Default for reasoning models
         messages: [],
         traces: [],
         outgoingTraces: [],
@@ -86,12 +95,44 @@ function Flow() {
   };
 
   const onNodeClick = (_: any, node: Node) => {
+    // Don't select disabled nodes
+    const nodeData = node.data as any;
+    if (nodeData?.disabled) return;
     setSelectedNode(node.id);
   };
 
+  const onDragOver = (event: React.DragEvent) => {
+    event.preventDefault();
+    event.dataTransfer.dropEffect = 'copy';
+  };
+
+  const onDrop = (event: React.DragEvent) => {
+    event.preventDefault();
+    
+    const archiveId = event.dataTransfer.getData('archiveId');
+    if (!archiveId) return;
+    
+    const bounds = reactFlowWrapper.current?.getBoundingClientRect();
+    if (!bounds) return;
+    
+    const position = project({
+      x: event.clientX - bounds.left,
+      y: event.clientY - bounds.top
+    });
+    
+    createNodeFromArchive(archiveId, position);
+  };
+
   return (
     <div style={{ width: '100vw', height: '100vh', display: 'flex' }}>
-      <div style={{ flex: 1, height: '100%' }} ref={reactFlowWrapper}>
+      <LeftSidebar isOpen={isLeftOpen} onToggle={() => setIsLeftOpen(!isLeftOpen)} />
+      
+      <div 
+        style={{ flex: 1, height: '100%', position: 'relative' }} 
+        ref={reactFlowWrapper}
+        onDragOver={onDragOver}
+        onDrop={onDrop}
+      >
         <ReactFlow
           nodes={nodes}
           edges={edges}
@@ -139,28 +180,64 @@ function Flow() {
                     }
                   } 
                 }
-              ] : 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) 
+              ] : menu.type === 'node' ? (() => {
+                const targetNode = nodes.find(n => n.id === menu.id);
+                const isDisabled = targetNode?.data?.disabled;
+                
+                // If disabled, only show Enable option
+                if (isDisabled) {
+                  return [
+                    { 
+                      label: 'Enable Node',
+                      onClick: () => menu.id && toggleNodeDisabled(menu.id)
+                    }
+                  ];
                 }
-              ]
+                
+                // Normal node menu
+                return [
+                  { 
+                    label: 'Disable Node',
+                    onClick: () => menu.id && toggleNodeDisabled(menu.id)
+                  },
+                  {
+                    label: 'Add to Archive',
+                    onClick: () => menu.id && archiveNode(menu.id)
+                  },
+                  { 
+                    label: 'Delete Node (Cascade)', 
+                    danger: true,
+                    onClick: () => menu.id && deleteBranch(menu.id)
+                  }
+                ];
+              })() : (() => {
+                // Check if any node connected to this edge is disabled
+                const targetEdge = edges.find(e => e.id === menu.id);
+                const sourceNode = nodes.find(n => n.id === targetEdge?.source);
+                const targetNode = nodes.find(n => n.id === targetEdge?.target);
+                const isTraceDisabled = sourceNode?.data?.disabled || targetNode?.data?.disabled;
+                
+                return [
+                  {
+                    label: isTraceDisabled ? 'Enable Trace' : 'Disable Trace',
+                    onClick: () => menu.id && toggleTraceDisabled(menu.id)
+                  },
+                  { 
+                    label: 'Disconnect', 
+                    onClick: () => menu.id && deleteEdge(menu.id) 
+                  },
+                  { 
+                    label: 'Delete Branch', 
+                    danger: true,
+                    onClick: () => menu.id && deleteBranch(undefined, menu.id) 
+                  }
+                ];
+              })()
             }
           />
         )}
       </div>
-      <Sidebar />
+      <Sidebar isOpen={isRightOpen} onToggle={() => setIsRightOpen(!isRightOpen)} />
     </div>
   );
 }
-- 
cgit v1.2.3

