Add flow view reordering via connection breaking

When a connection is broken in the flow view:
1. The node that lost its input moves to the end of the chain
2. The remaining nodes reconnect automatically
3. Tool model updates to reflect new order
4. Dependency validation warns about broken references

Flow: Input → A → B → C → Output
Break B's input: Input → A → C → B → Output

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2026-01-15 23:50:18 -04:00
parent 260ebf1b2f
commit d1f0c2f893
2 changed files with 84 additions and 0 deletions

View File

@ -241,6 +241,7 @@ class ToolBuilderPage(QWidget):
self._flow_widget = FlowGraphWidget()
self._flow_widget.node_double_clicked.connect(self._on_flow_node_double_clicked)
self._flow_widget.steps_deleted.connect(self._on_flow_steps_deleted)
self._flow_widget.steps_reordered.connect(self._on_flow_steps_reordered)
# Replace placeholder
old_widget = self.steps_stack.widget(1)
@ -282,6 +283,35 @@ class ToolBuilderPage(QWidget):
# Refresh the list view (flow view will be refreshed by set_tool)
self._refresh_steps()
def _on_flow_steps_reordered(self, new_order: list):
"""Handle step reordering from flow view.
Args:
new_order: List of old indices in new order.
e.g., [0, 2, 1] means step 0 stays first,
step 2 moves to second, step 1 moves to third.
"""
if not self._tool or not self._tool.steps:
return
# Reorder steps according to new_order
old_steps = self._tool.steps[:]
new_steps = [old_steps[i] for i in new_order if i < len(old_steps)]
# Validate the new order
warnings = self._validate_step_order(new_steps)
if warnings:
# Show warning but allow the reorder
warning_msg = "Variable dependency warnings:\n\n" + "\n".join(warnings)
warning_msg += "\n\nThe reorder has been applied. You may need to fix these issues."
QMessageBox.warning(self, "Dependency Warning", warning_msg)
self._tool.steps = new_steps
# Refresh both views
self._refresh_steps()
def _load_tool(self, name: str):
"""Load an existing tool for editing."""
tool = load_tool(name)

View File

@ -125,6 +125,9 @@ class FlowGraphWidget(QWidget):
# Emitted when steps are deleted from flow view (list of step indices)
steps_deleted = Signal(list)
# Emitted when steps are reordered (new order as list of step indices)
steps_reordered = Signal(list)
def __init__(self, parent=None):
super().__init__(parent)
self._tool: Optional[Tool] = None
@ -153,6 +156,7 @@ class FlowGraphWidget(QWidget):
# Connect signals
self._graph.node_double_clicked.connect(self._on_node_double_clicked)
self._graph.nodes_deleted.connect(self._on_nodes_deleted)
self._graph.port_disconnected.connect(self._on_port_disconnected)
# Add graph widget
layout.addWidget(self._graph.widget, 1)
@ -427,6 +431,56 @@ class FlowGraphWidget(QWidget):
self.steps_deleted.emit(deleted_indices)
self.flow_changed.emit()
def _on_port_disconnected(self, input_port, output_port):
"""Handle port disconnection - reorder steps.
When a connection is broken:
1. The node that lost its input moves to the end
2. The chain reconnects (previous node next node)
3. Disconnected node attaches at the end before Output
"""
if not self._tool or not self._tool.steps:
return
# Get the nodes involved
# input_port belongs to the node that lost its input
# output_port belongs to the node that was providing the input
disconnected_node = input_port.node()
source_node = output_port.node()
# Only handle step nodes (not Input/Output)
if not hasattr(disconnected_node, '_step_index') or disconnected_node._step_index < 0:
return
disconnected_idx = disconnected_node._step_index
# Find the node that the disconnected node was connected to (its output target)
next_node = None
if disconnected_node.output_ports():
out_port = disconnected_node.output_ports()[0]
connected_ports = out_port.connected_ports()
if connected_ports:
next_node = connected_ports[0].node()
# Build new order: move disconnected step to end
old_order = list(range(len(self._tool.steps)))
old_order.remove(disconnected_idx)
old_order.append(disconnected_idx)
# Reconnect the graph visually
# Connect source to next (if next is a step node)
if next_node and hasattr(next_node, '_step_index') and next_node._step_index >= 0:
if source_node.output_ports() and next_node.input_ports():
source_node.output_ports()[0].connect_to(next_node.input_ports()[0])
# Connect the last step (before disconnected) to disconnected node
# and disconnected node to output
# This will be handled by the rebuild after reorder
# Emit the new order
self.steps_reordered.emit(old_order)
self.flow_changed.emit()
def refresh(self):
"""Refresh the graph from current tool data."""
self._rebuild_graph()