diff --git a/src/components/WorkflowCanvas.tsx b/src/components/WorkflowCanvas.tsx index b8ac03e2..c3a01947 100644 --- a/src/components/WorkflowCanvas.tsx +++ b/src/components/WorkflowCanvas.tsx @@ -116,6 +116,61 @@ const isMouseWheel = (event: WheelEvent): boolean => { Math.abs(event.deltaY) % 40 === 0; // Mouse deltas often in multiples }; +// Check if an element can scroll and has room to scroll in the given direction +const canElementScroll = (element: HTMLElement, deltaX: number, deltaY: number): boolean => { + const style = window.getComputedStyle(element); + const overflowY = style.overflowY; + const overflowX = style.overflowX; + + const canScrollY = overflowY === 'auto' || overflowY === 'scroll'; + const canScrollX = overflowX === 'auto' || overflowX === 'scroll'; + + // Check if there's room to scroll in the delta direction + if (canScrollY && deltaY !== 0) { + const hasVerticalScroll = element.scrollHeight > element.clientHeight; + if (hasVerticalScroll) { + // Check if we can scroll further in the delta direction + if (deltaY > 0 && element.scrollTop < element.scrollHeight - element.clientHeight) { + return true; // Can scroll down + } + if (deltaY < 0 && element.scrollTop > 0) { + return true; // Can scroll up + } + } + } + + if (canScrollX && deltaX !== 0) { + const hasHorizontalScroll = element.scrollWidth > element.clientWidth; + if (hasHorizontalScroll) { + if (deltaX > 0 && element.scrollLeft < element.scrollWidth - element.clientWidth) { + return true; // Can scroll right + } + if (deltaX < 0 && element.scrollLeft > 0) { + return true; // Can scroll left + } + } + } + + return false; +}; + +// Find if the target element or any ancestor is scrollable +const findScrollableAncestor = (target: HTMLElement, deltaX: number, deltaY: number): HTMLElement | null => { + let current: HTMLElement | null = target; + + while (current && !current.classList.contains('react-flow')) { + // Check for nowheel class (React Flow convention for elements that should handle their own scroll) + if (current.classList.contains('nowheel') || current.tagName === 'TEXTAREA') { + if (canElementScroll(current, deltaX, deltaY)) { + return current; + } + } + current = current.parentElement; + } + + return null; +}; + export function WorkflowCanvas() { const { nodes, edges, groups, onNodesChange, onEdgesChange, onConnect, addNode, updateNodeData, loadWorkflow, getNodeById, addToGlobalHistory, setNodeGroupId } = useWorkflowStore(); @@ -520,6 +575,15 @@ export function WorkflowCanvas() { // Custom wheel handler for macOS trackpad support const handleWheel = useCallback((event: React.WheelEvent) => { + // Check if scrolling over a scrollable element (e.g., textarea, scrollable div) + const target = event.target as HTMLElement; + const scrollableElement = findScrollableAncestor(target, event.deltaX, event.deltaY); + + if (scrollableElement) { + // Let the element handle its own scroll - don't prevent default or manipulate viewport + return; + } + // Pinch gesture (ctrlKey) always zooms if (event.ctrlKey) { event.preventDefault(); diff --git a/src/components/nodes/LLMGenerateNode.tsx b/src/components/nodes/LLMGenerateNode.tsx index 72c7faf9..f61ff0f7 100644 --- a/src/components/nodes/LLMGenerateNode.tsx +++ b/src/components/nodes/LLMGenerateNode.tsx @@ -97,7 +97,7 @@ export function LLMGenerateNode({ id, data, selected }: NodeProps {/* Output preview area */} -
+
{nodeData.status === "loading" ? (