Add flow visualization for tool builder

Implements node-based flow visualization using NodeGraphQt-QuiltiX-fork:

New files:
- src/cmdforge/gui/widgets/flow_graph.py: FlowGraphWidget with custom node types
  - InputNode: Shows tool inputs ($input + argument variables)
  - PromptNode: AI prompt steps with provider display
  - CodeNode: Python code steps
  - OutputNode: Final output
- scripts/test_nodegraph.py: Standalone prototype for testing

Modified files:
- tool_builder_page.py: Added List/Flow view toggle with QStackedWidget
  - Lazy-loads flow widget on first use
  - Syncs between list and flow views
  - Double-click nodes to edit steps
- styles.py: Added viewToggle button styling
- widgets/__init__.py: Export FlowGraphWidget
- pyproject.toml: Added 'flow' optional dependency group

Features:
- Toggle between List and Flow views in Steps panel
- Nodes auto-connected based on step order
- Auto-layout and fit-to-view on load
- Double-click nodes opens step edit dialog
- Graceful fallback if NodeGraphQt not installed

Co-Authored-By: Claude Opus 4.5 <[email protected]>
This commit is contained in:
rob
2026-01-15 17:07:35 -04:00
co-authored by Claude Opus 4.5
parent a5e0948881
commit 042be8b49b
6 changed files with 499 additions and 5 deletions
+127
View File
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
"""Test script for NodeGraphQt integration."""
import sys
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from NodeGraphQt import NodeGraph, BaseNode
class InputNode(BaseNode):
"""Node representing tool input."""
__identifier__ = 'cmdforge.nodes'
NODE_NAME = 'Input'
def __init__(self):
super().__init__()
self.set_color(100, 100, 180) # Blue-ish
self.add_output('input', color=(180, 180, 250))
self.add_output('file', color=(180, 180, 250))
class PromptNode(BaseNode):
"""Node representing a prompt step."""
__identifier__ = 'cmdforge.nodes'
NODE_NAME = 'Prompt'
def __init__(self):
super().__init__()
self.set_color(80, 120, 180) # Indigo-ish
self.add_input('in', color=(180, 180, 250))
self.add_output('response', color=(180, 250, 180))
# Add properties
self.add_text_input('provider', 'Provider', text='claude')
self.add_text_input('output_var', 'Output Var', text='response')
class CodeNode(BaseNode):
"""Node representing a code step."""
__identifier__ = 'cmdforge.nodes'
NODE_NAME = 'Code'
def __init__(self):
super().__init__()
self.set_color(80, 160, 100) # Green-ish
self.add_input('in', color=(180, 250, 180))
self.add_output('result', color=(250, 220, 180))
# Add properties
self.add_text_input('output_var', 'Output Var', text='result')
class OutputNode(BaseNode):
"""Node representing tool output."""
__identifier__ = 'cmdforge.nodes'
NODE_NAME = 'Output'
def __init__(self):
super().__init__()
self.set_color(180, 120, 80) # Orange-ish
self.add_input('in', color=(250, 220, 180))
def main():
app = QApplication(sys.argv)
# Create main window
window = QMainWindow()
window.setWindowTitle('CmdForge Flow Visualization - Prototype')
window.setGeometry(100, 100, 1000, 700)
# Create node graph
graph = NodeGraph()
# Register our custom nodes
graph.register_node(InputNode)
graph.register_node(PromptNode)
graph.register_node(CodeNode)
graph.register_node(OutputNode)
# Get the graph widget
graph_widget = graph.widget
# Set as central widget
window.setCentralWidget(graph_widget)
# Create a sample tool visualization
input_node = graph.create_node('cmdforge.nodes.InputNode', name='Input')
input_node.set_pos(-300, 0)
prompt_node = graph.create_node('cmdforge.nodes.PromptNode', name='Summarize')
prompt_node.set_pos(-50, 0)
prompt_node.set_property('provider', 'claude')
prompt_node.set_property('output_var', 'response')
code_node = graph.create_node('cmdforge.nodes.CodeNode', name='Format')
code_node.set_pos(200, 0)
code_node.set_property('output_var', 'formatted')
output_node = graph.create_node('cmdforge.nodes.OutputNode', name='Output')
output_node.set_pos(450, 0)
# Connect nodes
input_node.output(0).connect_to(prompt_node.input(0))
prompt_node.output(0).connect_to(code_node.input(0))
code_node.output(0).connect_to(output_node.input(0))
# Auto-layout
graph.auto_layout_nodes()
# Fit view
graph.fit_to_selection()
window.show()
print("Flow visualization prototype running...")
print("Nodes created: Input -> Prompt -> Code -> Output")
print("Close the window to exit.")
sys.exit(app.exec())
if __name__ == '__main__':
main()