import { html, css, svg } from 'lit'; export const connectionStyles = css` .connections { pointer-events: none; } .connections path { transition: stroke 0.3s ease, stroke-width 0.3s ease; } .connections path:hover { stroke: #999; stroke-width: 3; } `; export const connections = function(superClass) { return class extends superClass { constructor() { super(); this.connections = []; this.draggingConnection = null; this.mousePosition = { x: 0, y: 0 }; } firstUpdated() { this.addEventListener('port-click', this._handlePortClick); document.addEventListener('mousemove', this._updatePreviewConnection.bind(this)); super.firstUpdated(); } _updatePreviewConnection(e) { if (this.draggingConnection) { this.mousePosition = { x: e.clientX, y: e.clientY }; this.requestUpdate(); } } _renderConnections() { return html` ${this.connections.map(conn => this._renderConnection(conn))} ${this.draggingConnection ? this._renderPreviewConnection() : null} `; } _renderPreviewConnection() { const sourceNode = this.nodes.find(node => node.id === this.draggingConnection.sourceNodeId); if (!sourceNode) return ''; const sourcePort = sourceNode.shadowRoot.querySelector( `.output-ports [data-port-id="${this.draggingConnection.sourcePortId}"]` ); if (!sourcePort) return ''; const sourceRect = sourcePort.getBoundingClientRect(); const canvasRect = this.canvas.getBoundingClientRect(); const start = { x: sourceRect.left + sourceRect.width/2 - canvasRect.left, y: sourceRect.top + sourceRect.height/2 - canvasRect.top }; const end = { x: this.mousePosition.x - canvasRect.left, y: this.mousePosition.y - canvasRect.top }; const controlPoint1 = { x: start.x + Math.min(100, Math.abs(end.x - start.x) / 2), y: start.y }; const controlPoint2 = { x: end.x - Math.min(100, Math.abs(end.x - start.x) / 2), y: end.y }; const path = `M${start.x},${start.y} C${controlPoint1.x},${controlPoint1.y} ${controlPoint2.x},${controlPoint2.y} ${end.x},${end.y}`; return svg` `; } _renderConnection(conn) { const sourceNode = this.nodes.find(node => node.id === conn.sourceNodeId); const targetNode = this.nodes.find(node => node.id === conn.targetNodeId); if (!sourceNode || !targetNode) return ''; const sourcePort = sourceNode.shadowRoot.querySelector(`.output-ports [data-port-id="${conn.sourcePortId}"]`); const targetPort = targetNode.shadowRoot.querySelector(`.input-ports [data-port-id="${conn.targetPortId}"]`); if (!sourcePort || !targetPort) return ''; const sourceRect = sourcePort.getBoundingClientRect(); const targetRect = targetPort.getBoundingClientRect(); const canvasRect = this.canvas.getBoundingClientRect(); const start = { x: sourceRect.left + sourceRect.width/2 - canvasRect.left, y: sourceRect.top + sourceRect.height/2 - canvasRect.top }; const end = { x: targetRect.left + targetRect.width/2 - canvasRect.left, y: targetRect.top + targetRect.height/2 - canvasRect.top }; const controlPoint1 = { x: start.x + Math.min(100, Math.abs(end.x - start.x) / 2), y: start.y }; const controlPoint2 = { x: end.x - Math.min(100, Math.abs(end.x - start.x) / 2), y: end.y }; const path = `M${start.x},${start.y} C${controlPoint1.x},${controlPoint1.y} ${controlPoint2.x},${controlPoint2.y} ${end.x},${end.y}`; return svg` `; } _handlePortClick(e) { const { nodeId, portType, portId, portName } = e.detail; if (!this.draggingConnection) { if (portType === 'output') { this.draggingConnection = { sourceNodeId: nodeId, sourcePortId: portId, sourcePortType: portType }; } } else { if (portType === 'input' && nodeId !== this.draggingConnection.sourceNodeId) { const connectionExists = this.connections.some(conn => conn.sourceNodeId === this.draggingConnection.sourceNodeId && conn.sourcePortId === this.draggingConnection.sourcePortId && conn.targetNodeId === nodeId && conn.targetPortId === portId ); if (!connectionExists) { const connection = { id: `conn_${Date.now()}`, sourceNodeId: this.draggingConnection.sourceNodeId, sourcePortId: this.draggingConnection.sourcePortId, targetNodeId: nodeId, targetPortId: portId }; this.connections = [...this.connections, connection]; } } this.draggingConnection = null; this.requestUpdate(); } } }; }