diff --git a/src/App.tsx b/src/App.tsx index cd406c8..9e41673 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1501,14 +1501,30 @@ function App() { } }) .then((dataUrl) => { + let id = `flow-${Date.now()}`; + const validMetadata = { + ...metadata, + id: id, + timestamp: new Date().toISOString(), + }; const a = document.createElement("a"); - a.setAttribute("download", "reactflow-diagram.svg"); + const filename = `${validMetadata.name.replace(/\s+/g, "_")}_${validMetadata.id}.svg` + a.setAttribute("download", filename); a.setAttribute("href", dataUrl); a.click(); }) .catch((err) => console.error("Error exporting SVG:", err)); }; + const onExperienceLevelChange = (event) => { + setExperienceLevel(event); + setExperienceLevelOn(event); + const bool_value = (experienceLevel === "pioneer")?false:true; // if previous experience level was pioneer... + setCompactVisualization(bool_value) + setAncillaMode(bool_value) + setAncillaModelingOn(bool_value) + }; + return ( { setAncillaModelingOn(!ancillaModelingOn); setAncillaMode(!ancillaModelingOn) }} experienceLevel={experienceLevel} - onExperienceLevelChange={(event) => { setExperienceLevel(event); setExperienceLevelOn(event); }} + onExperienceLevelChange={onExperienceLevelChange} compactVisualization={compactVisualization} onCompactVisualizationChange={() => { setCompactVisualization(!compact); setCompact(!compact) }} completionGuaranteed={completionGuaranteed} onCompletionGuaranteedChange={setCompletionGuaranteed} /> + { if (node.type === 'dataTypeNode' || node.type === "classicalAlgorithmNode" || node.type === ClassicalOperatorNode) return 'minimap-node-circle'; diff --git a/src/components/contextMenu.tsx b/src/components/contextMenu.tsx index 26d1a8d..c16d471 100644 --- a/src/components/contextMenu.tsx +++ b/src/components/contextMenu.tsx @@ -1,7 +1,9 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useState } from "react"; import { useReactFlow, Node } from "reactflow"; -import { FaTrash, FaCopy } from "react-icons/fa"; +import { FaTrash, FaCopy, FaPlus, FaMinus, FaReact, FaCaretRight} from "react-icons/fa"; +import { FaGear } from "react-icons/fa6"; import { v4 as uuid } from "uuid"; +import { Button } from "./ui"; interface ContextMenuProps { id: string; @@ -27,6 +29,8 @@ export const ContextMenu: React.FC = ({ styles = {}, // Default styles as an empty object }) => { const { getNode, setNodes, addNodes, setEdges } = useReactFlow(); + const type = getNode(id).type + console.log("type", type) const duplicateNode = useCallback(() => { // duplicate node at slighlty shifted position const node = getNode(id); @@ -54,46 +58,529 @@ export const ContextMenu: React.FC = ({ if (onAction) onAction("delete", id); }, [id, setNodes, setEdges, onAction]); - return ( -
-

- Node: {id} -

-
+ const incrementNodeDataField = useCallback((field: string, min_value: number) => { //TODO: richtiges Feld verwenden (bei merger: numberInputs) --> Feld als Argument übergeben? + const node = getNode(id); + //const field = "numberQuantumInputs"; + console.log("[INCREMENT] field value", node.data[field], "field value type", typeof node.data[field]) + setNodes((nodes) => + nodes.map((n) => + n.id === id + ? { ...n, data: { ...n.data, [field]: n.data[field]? Number(n.data[field]) + 1 : min_value+1 } } + : n + ) + ); + }, [id, getNode, setNodes, onAction]); + + const decrementNodeDataField = useCallback((field: string, min_value: number) => { + const node = getNode(id); + console.log("[DECREMENT] field value", node.data[field], "field value type", typeof node.data[field]) + setNodes((nodes) => + nodes.map((n) => + n.id === id + ? { ...n, data: { ...n.data, [field]: Number(n.data[field])>min_value? Number(n.data[field]) - 1 : min_value } } + : n + ) + ); + }, [id, getNode, setNodes, onAction]); + const [submenuInputAddOpen, setSubmenuInputAddOpen] = useState(false); + const [submenuInputRemoveOpen, setSubmenuInputRemoveOpen] = useState(false); + const [submenuOutputAddOpen, setSubmenuOutputAddOpen] = useState(false); + const [submenuOutputRemoveOpen, setSubmenuOutputRemoveOpen] = useState(false); + var minInput = 0; + var minOutput = 0; + if (type === "mergerNode" || type === "splitterNode"){ + minInput = 2; + minOutput = 2; + } else if (type === "ifElseNode" || type === "controlStructureNode") { + minInput = 1; + } + + const hasQuantumInputs = getNode(id).data["numberQuantumInputs"]>minInput // what happens if node has no filed numQuantumInputs? + const hasClassicalInputs = getNode(id).data["numberClassicalInputs"]>minInput + const hasQuantumOutputs = getNode(id).data["numberQuantumOutputs"]>minOutput // what happens if node has no filed numQuantumInputs? + const hasClassicalOutputs = getNode(id).data["numberClassicalOutputs"]>minOutput + const hasInputs = getNode(id).data["numberInputs"]>minInput + const hasOutputs = getNode(id).data["numberOutputs"]>minOutput + + console.log("contextMenu") + console.log(getNode(id).type) + console.log(getNode(id).data["numberQuantumInputs"]) + console.log(hasQuantumInputs) + console.log(hasClassicalInputs) + + + const toggleSubmenuInputAdd = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); // prevent closing parent + setSubmenuInputAddOpen(prev => !prev); + console.log("submenuAdd", submenuInputAddOpen); + }, []); + + const toggleSubmenuInputRemove = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); // prevent closing parent + setSubmenuInputRemoveOpen(prev => !prev); + }, []); + + const toggleSubmenuOutputAdd = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); // prevent closing parent + setSubmenuOutputAddOpen(prev => !prev); + console.log("submenuAdd", submenuInputAddOpen); + }, []); + + const toggleSubmenuOutputRemove = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); // prevent closing parent + setSubmenuOutputRemoveOpen(prev => !prev); + }, []); + + let buttons = []; + // every node can be duplicated and deleted + buttons.push( + ); + buttons.push( + ); + console.log("buttons", buttons) + + if (type === "ifElseNode" || type === "controlStructureNode"){ + return ( +
+

+ Node: {id} +

+
setSubmenuInputAddOpen(true)} + onMouseLeave={() => setSubmenuInputAddOpen(false)}> + {submenuInputAddOpen && ( +
+ + +
+ )} +
+ {(hasQuantumInputs || hasClassicalInputs) &&
setSubmenuInputRemoveOpen(true)} + onMouseLeave={() => setSubmenuInputRemoveOpen(false)}> + + {submenuInputRemoveOpen && ( +
+ {hasQuantumInputs && } + {hasClassicalInputs && } +
+ )} +
} + {buttons} +
+ ); + } else if (type === "algorithmNode") { + return ( +
+

+ Node: {id} +

+
setSubmenuInputAddOpen(true)} + onMouseLeave={() => setSubmenuInputAddOpen(false)}> + {submenuInputAddOpen && ( +
+ + +
+ )}
-
- ); +
setSubmenuOutputAddOpen(true)} + onMouseLeave={() => setSubmenuOutputAddOpen(false)}> + + {submenuOutputAddOpen && ( +
+ + +
+ )} +
+ {(hasQuantumInputs || hasClassicalInputs) &&
setSubmenuInputRemoveOpen(true)} + onMouseLeave={() => setSubmenuInputRemoveOpen(false)}> + + {submenuInputRemoveOpen && ( +
+ {hasQuantumInputs && } + {hasClassicalInputs && } +
+ )} +
} + {(hasQuantumOutputs || hasClassicalOutputs) &&
setSubmenuOutputRemoveOpen(true)} + onMouseLeave={() => setSubmenuOutputRemoveOpen(false)}> + + {submenuOutputRemoveOpen && ( +
+ {hasQuantumOutputs && } + {hasClassicalOutputs && } +
+ )} +
} + {buttons} +
+ ); + } else if (type === "classicalAlgorithmNode") { +return ( +
+

+ Node: {id} +

+
+ + + {hasClassicalInputs && } + {hasClassicalOutputs && } +
+
+ {buttons} +
+
+ ); + } else if (type === "mergerNode"){ +return ( +
+

+ Node: {id} +

+
+ + {hasInputs && } +
+
+ {buttons} +
+
+ ); + } else if (type === "splitterNode"){ +return ( +
+

+ Node: {id} +

+
+ + {hasOutputs && } +
+
+ {buttons} +
+
+ ); + } else { + return ( +
+

+ Node: {id} +

+
+ {buttons} +
+
+ ); + } }; const buttonStyle: React.CSSProperties = { @@ -115,3 +602,17 @@ const iconStyle: React.CSSProperties = { marginRight: "8px", fontSize: "16px", }; + +const submenuStyle: React.CSSProperties = { + position: 'absolute', + top: 0, + left: '100%', + backgroundColor: "#fff", + border: "1px solid #ddd", + borderRadius: "8px", + boxShadow: "0px 4px 8px rgba(0, 0, 0, 0.1)", + padding: "10px", + zIndex: 1000, + display: "flex", + flexDirection: "column", +}; diff --git a/src/components/modals/configModal.tsx b/src/components/modals/configModal.tsx index 1845fa6..7f024fc 100644 --- a/src/components/modals/configModal.tsx +++ b/src/components/modals/configModal.tsx @@ -194,7 +194,7 @@ export const ConfigModal = ({ className="w-4 h-4" /> - {localCompactVisualization ? "True" : "False"} + {localCompactVisualization ? "On" : "Off"} @@ -214,7 +214,7 @@ export const ConfigModal = ({ className="w-4 h-4" /> - {localCompletionGuaranteed ? "True" : "False"} + {localCompletionGuaranteed ? "Yes" : "No"} diff --git a/src/components/modals/experienceLevelModal.tsx b/src/components/modals/experienceLevelModal.tsx index 953bd77..5d22d01 100644 --- a/src/components/modals/experienceLevelModal.tsx +++ b/src/components/modals/experienceLevelModal.tsx @@ -140,12 +140,12 @@ export default function ExperienceModePanel({ Explorer - No + Off Off Pioneer - Yes + On On diff --git a/src/components/nodes/algorithm.tsx b/src/components/nodes/algorithm.tsx index 7f468d9..92aa5d1 100644 --- a/src/components/nodes/algorithm.tsx +++ b/src/components/nodes/algorithm.tsx @@ -7,7 +7,7 @@ import UncomputePort from "../utils/uncomputePort"; import OutputPort from "../utils/outputPort"; import { motion } from "framer-motion"; import { cn } from "@/lib/utils"; -import { ancillaConstructColor, dirtyAncillaHandle, dirtyConstructColor, quantumConstructColor } from "@/constants"; +import { ancillaConstructColor, classicalConstructColor, dirtyAncillaHandle, dirtyConstructColor, quantumConstructColor } from "@/constants"; import { findDuplicateOutputIdentifier, findDuplicateOutputIdentifiers, findDuplicateOutputIdentifiersInsideNode } from "../utils/utils"; import { AlertCircle } from "lucide-react"; @@ -34,17 +34,20 @@ const selector = (state: { export const AlgorithmNode = memo((node: Node) => { const { data, selected } = node; console.log(selected) - console.log(data.numberInputs) - const numberInputs = data.numberInputs || 0; - const numberOutputs = data.numberOutputs || 0; + console.log(data.numberQuantumInputs) + const numberQuantumInputs = data.numberQuantumInputs || 0; + const numberQuantumOutputs = data.numberQuantumOutputs || 0; + const numberClassicalInputs = data.numberClassicalInputs || 0; + const numberClassicalOutputs = data.numberClassicalOutputs || 0; - const handleCount = Math.max(numberInputs, numberOutputs); - console.log(handleCount) + const inputHandleCount = numberQuantumInputs + numberClassicalInputs; + const outputHandleCount = numberQuantumOutputs+numberClassicalOutputs; + //console.log(handleCount) const handleGap = 40; const handleOffset = 15; - const nodeHeight = Math.max(handleOffset * 2 + (handleCount) * handleGap, 100); + const nodeHeight = Math.max(handleOffset * 2 + inputHandleCount * handleGap + outputHandleCount * 80, 100); const { edges, nodes, updateNodeValue, setSelectedNode, setNewEdges, setEdges, ancillaMode } = useStore( selector, shallow @@ -58,10 +61,12 @@ export const AlgorithmNode = memo((node: Node) => { const [outputIdentifierError, setOutputIdentifierError] = useState(false); const [showingChildren, setShowingChildren] = useState(false); const [sizeErrors, setSizeErrors] = useState<{ [key: number]: boolean }>({}); + const [classicalSizeErrors, setClassicalSizeErrors] = useState<{ [key: number]: boolean }>({}); const [isEditingLabel, setIsEditingLabel] = useState(false); const [editableLabel, setEditableLabel] = useState(data.label || ""); const updateNodeInternals = useUpdateNodeInternals(); const [outputIdentifierErrors, setOutputIdentifierErrors] = useState({}); + const [classicalOutputIdentifierErrors, setClassicalOutputIdentifierErrors] = useState({}); const handleLabelChange = () => { setIsEditingLabel(false); @@ -88,12 +93,14 @@ export const AlgorithmNode = memo((node: Node) => { useEffect(() => { const identifier = node.data.outputIdentifier; console.log(nodes) - let selectedNode = nodes.find(n => n.id === node.id); + //let selectedNode = nodes.find(n => n.id === node.id); + let selectedNode = node; const newErrors = {}; + console.log("ALGORITHM", outputs) outputs.forEach((output, index) => { const outputIdentifier = output?.identifier?.trim(); - console.log(outputIdentifier) + console.log("Outputidentifier", outputIdentifier) const size = output?.size?.trim(); if (!outputIdentifier) return; const duplicates = findDuplicateOutputIdentifier(nodes, selectedNode.id, selectedNode, outputIdentifier); @@ -132,7 +139,7 @@ export const AlgorithmNode = memo((node: Node) => { const baseHeight = !ancillaMode ? 50 : 200; const extraHeightPerVariable = 130; const dynamicHeight = - baseHeight + 2 * 40 + 3 * 50 + (numberInputs * 50) + numberOutputs * extraHeightPerVariable; + baseHeight + 2 * 40 + 3 * 50 + (inputHandleCount * 50) + outputHandleCount * extraHeightPerVariable; // Ensure identifiers exist and match the number of outputs if (!data.identifiers) { @@ -140,19 +147,19 @@ export const AlgorithmNode = memo((node: Node) => { } // Add missing identifiers - while (data.identifiers.length < numberOutputs) { + while (data.identifiers.length < outputHandleCount) { data.identifiers.push("q" + Math.floor(100000 + Math.random() * 900000).toString()); } // Remove extra identifiers - if (data.identifiers.length > numberOutputs) { - const removedIdentifiers = data.identifiers.slice(numberOutputs); + if (data.identifiers.length > outputHandleCount) { + const removedIdentifiers = data.identifiers.slice(outputHandleCount); console.log(removedIdentifiers); // Clean up edges with sourceHandles related to removed identifiers const edgesToRemove = edges.filter((edge) => !removedIdentifiers.some((id, index) => - edge.sourceHandle === `quantumHandleGateOutput${numberOutputs + index + 1}${node.id}` + edge.sourceHandle === `quantumHandleGateOutput${numberQuantumOutputs + index + 1}${node.id}` ) ); console.log("EDGES") @@ -163,7 +170,7 @@ export const AlgorithmNode = memo((node: Node) => { setNewEdges(edgesToRemove); } - data.identifiers = data.identifiers.slice(0, numberOutputs); + data.identifiers = data.identifiers.slice(0, outputHandleCount); } console.log(nodeHeight) @@ -245,23 +252,25 @@ export const AlgorithmNode = memo((node: Node) => {
- {Array.from({ length: numberInputs }).map((_, index) => ( + {Array.from({ length: numberClassicalInputs }).map((_, index) => (
@@ -269,6 +278,30 @@ export const AlgorithmNode = memo((node: Node) => {
))} + {Array.from({ length: numberQuantumInputs }).map((_, index) => ( +
+ + + {node.data.inputs?.[index]?.outputIdentifier || `Input ${index + 1}`} + +
+ ))}
{ancillaMode && (
@@ -342,24 +375,47 @@ export const AlgorithmNode = memo((node: Node) => {
- {Array.from({ length: numberOutputs }).map((_, index) => ( + {Array.from({length: numberClassicalOutputs}).map((_, index) => ( + setOutputIdentifierErrors(prev => ({ ...prev, [index]: error }))} + setSizeError={(error) => + setClassicalSizeErrors((prev) => ({ ...prev, [index]: error }))} + setSelectedNode={setSelectedNode} + active={true} + /> + )) + + } + {Array.from({ length: numberQuantumOutputs }).map((_, index) => ( + - setOutputIdentifierErrors(prev => ({ ...prev, [index]: error })) + setOutputIdentifierErrors(prev => ({ ...prev, [numberClassicalOutputs+index]: error })) } - sizeError={sizeErrors[index]} + sizeError={sizeErrors[numberClassicalOutputs+index]} setSizeError={(error) => - setSizeErrors((prev) => ({ ...prev, [index]: error })) + setSizeErrors((prev) => ({ ...prev, [numberClassicalOutputs+index]: error })) } setSelectedNode={setSelectedNode} active={true} @@ -370,9 +426,9 @@ export const AlgorithmNode = memo((node: Node) => { {ancillaMode && (
- - - + + +
)}
diff --git a/src/components/nodes/classicalalgorithm.tsx b/src/components/nodes/classicalalgorithm.tsx index 10cf820..4bc1840 100644 --- a/src/components/nodes/classicalalgorithm.tsx +++ b/src/components/nodes/classicalalgorithm.tsx @@ -22,8 +22,8 @@ const selector = (state: any) => ({ export const ClassicalAlgorithmNode = memo((node: Node) => { const { data, selected } = node; - const numberInputs = data.numberInputs || 0; - const numberOutputs = data.numberOutputs || 0; + const numberInputs = data.numberClassicalInputs || 0; + const numberOutputs = data.numberClassicalOutputs || 0; const { edges, nodes, updateNodeValue, setSelectedNode, setNewEdges, ancillaMode } = useStore(selector, shallow); const updateNodeInternals = useUpdateNodeInternals(); @@ -55,7 +55,8 @@ export const ClassicalAlgorithmNode = memo((node: Node) => { }, [ancillaMode]); useEffect(() => { - const selectedNode = nodes.find(n => n.id === node.id); + //const selectedNode = nodes.find(n => n.id === node.id); + let selectedNode = node; const newErrors: { [key: number]: boolean } = {}; const newSizeErrors: { [key: number]: boolean } = {}; diff --git a/src/components/nodes/controlstructure.tsx b/src/components/nodes/controlstructure.tsx index eff1e02..e4a523a 100644 --- a/src/components/nodes/controlstructure.tsx +++ b/src/components/nodes/controlstructure.tsx @@ -23,41 +23,54 @@ const selector = (state: { }); export const ControlStructureNode = memo((node: Node) => { + const {data} = node const [showingChildren, setShowingChildren] = useState(false); const { setNodes, updateNodeValue, setSelectedNode, edges } = useStore(selector, shallow); const updateNodeInternals = useUpdateNodeInternals(); + const numberQuantumInputs = data.numberQuantumInputs || 1; + const numberClassicalInputs = data.numberClassicalInputs || 1; + const quantumHandles = Array.from({ length: numberQuantumInputs }, (_, index) => index); + const classicalHandles = Array.from({ length: numberClassicalInputs }, (_, index) => index); + const [quantumOutputHandles, setQuantumOutputHandles] = useState([0]); + const [classicalOutputHandles, setClassicalOutputHandles] = useState([0]); + const [condition, setCondition] = useState(""); - const [quantumHandles, setQuantumHandles] = useState([0]); - const [quantumOutputHandles, setQuantumOutputHandles] = useState([0]); - const [condition, setCondition] = useState(""); + console.log("quantumHandles", quantumHandles) + console.log("data.numberQuantumInputs", data.numberQuantumInputs) + console.log("numberQuantumInputs", numberQuantumInputs) useEffect(() => { - const connectedQuantumInputs = quantumHandles.filter((index) => - edges.some((edge) => edge.targetHandle === `quantumHandleInputInitialization${node.id}-${index}`) + // determine internal output handles (left side of right polygon) that are connected + const connectedQuantumOutputs = quantumHandles.filter((index) => + edges.some((edge) => edge.targetHandle === `quantumHandleInputDynamic${node.id}-${index}`) ); - const lastIndex = quantumHandles[quantumHandles.length - 1]; - const lastHandleId = `quantumHandleInputInitialization${node.id}-${lastIndex}`; - const isLastConnected = edges.some(edge => edge.targetHandle === lastHandleId); + const connectedClassicalOutputs = classicalHandles.filter((index) => + edges.some((edge) => edge.targetHandle === `classicalHandleInputDynamic${node.id}-${index}`) + ); + + //const lastIndex = quantumHandles[quantumHandles.length - 1]; + //const lastHandleId = `quantumHandleInputInitialization${node.id}-${lastIndex}`; + //const isLastConnected = edges.some(edge => edge.targetHandle === lastHandleId); // Add only if not already added - if (isLastConnected && quantumHandles.length === connectedQuantumInputs.length) { - setQuantumHandles((prev) => [...prev, prev.length]); - } + // if (isLastConnected && quantumHandles.length === connectedQuantumInputs.length) { + // setQuantumHandles((prev) => [...prev, prev.length]); + // } if (node.data.condition !== null) { //updateNodeValue(node.id, "condition", node.data.condition); } else { updateNodeValue(node.id, "condition", condition); } - setQuantumOutputHandles(connectedQuantumInputs); + setClassicalOutputHandles(connectedClassicalOutputs); + setQuantumOutputHandles(connectedQuantumOutputs); updateNodeInternals(node.id); }, [edges, node.id]); - - const dynamicHeight = 600 + Math.max(0, quantumHandles.length - 1) * 30; - const totalHandles = Math.max(quantumHandles.length, 0); + const dynamicHeight = 600 + Math.max(0, quantumHandles.length - 1 + (classicalHandles.length - 1)) * 30; + const totalHandles = Math.max(classicalHandles.length + quantumHandles.length, classicalOutputHandles.length + quantumOutputHandles.length); const hexagonHeight = Math.max(250, 180 + totalHandles * 30); const hexagonTopOffset = -(hexagonHeight / 2) + 20; @@ -132,6 +145,24 @@ export const ControlStructureNode = memo((node: Node) => {
{/* Handles - Left */} +
+ {classicalHandles.map((index, i) => { + const handleId = `classicalHandleInputInitialization${node.id}-${index}`; + const isConnected = edges.some(edge => edge.targetHandle === handleId); + return ( + + ); + })} +
{quantumHandles.map((index, i) => { const handleId = `quantumHandleInputInitialization${node.id}-${index}`; @@ -142,8 +173,8 @@ export const ControlStructureNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn("z-10 circle-port-hex-in", isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black")} - style={{ top: `${hexagonTopOffset + 100 + i * 30}px`, overflow: "visible" }} + className={"z-10 circle-port-hex-in !bg-blue-300 !border-black"} + style={{ top: `${hexagonTopOffset + 100 + classicalHandles.length * 30 + i * 30}px`, overflow: "visible" }} isConnectable={true} isConnectableStart={false} /> @@ -151,6 +182,30 @@ export const ControlStructureNode = memo((node: Node) => { })}
{/* Output Handles - Right side of the left polygon */} +
+ {classicalHandles.map((index, i) => { + const handleInputId = `classicalHandleInputInitialization${node.id}-${index}`; + const isInputConnected = edges.some(edge => edge.targetHandle === handleInputId); + const handleId = `classicalHandleOutputInitialization${node.id}-${index}`; + const isConnected = edges.some(edge => edge.sourceHandle === handleId); + console.log(isConnected) + return isInputConnected && ( + + ); + })} +
{quantumHandles.map((index, i) => { const handleInputId = `quantumHandleInputInitialization${node.id}-${index}`; @@ -164,9 +219,9 @@ export const ControlStructureNode = memo((node: Node) => { type="source" id={handleId} position={Position.Right} - className={cn("z-10 circle-port-hex-out", isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black")} + className={"z-10 circle-port-hex-out !bg-blue-300 !border-black"} style={{ - top: `${hexagonTopOffset + 100 + i * 30}px`, + top: `${hexagonTopOffset + 100 + classicalHandles.length * 30 + i * 30}px`, overflow: "visible" }} isConnectable={true} @@ -232,6 +287,26 @@ export const ControlStructureNode = memo((node: Node) => {
{/* Handles - Left side of the right polygon */} +
+ {classicalHandles.map((index, i) => { + const handleInputId = `classicalHandleInputInitialization${node.id}-${index}`; + const isInputConnected = edges.some(edge => edge.targetHandle === handleInputId); + const handleId = `classicalHandleInputDynamic${node.id}-${index}`; + const isConnected = edges.some(edge => edge.targetHandle === handleId); + return isInputConnected && ( + + ); + })} +
{quantumHandles.map((index, i) => { const handleInputId = `quantumHandleInputInitialization${node.id}-${index}`; @@ -244,11 +319,8 @@ export const ControlStructureNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 circle-port-hex-in", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} - style={{ top: `${hexagonTopOffset + 100 + i * 30}px`, overflow: "visible" }} + className={"z-10 circle-port-hex-in !bg-blue-300 !border-black"} + style={{ top: `${hexagonTopOffset + 100 + classicalHandles.length * 30 + i * 30}px`, overflow: "visible" }} isConnectable={true} isConnectableStart={false} /> @@ -257,23 +329,40 @@ export const ControlStructureNode = memo((node: Node) => {
{/* Handles - Right side of the right polygon */} +
+ {classicalHandles.map((index, i) => { + const handleOutputId = `classicalHandleInputDynamic${node.id}-${index}`; + const isOutputConnected = edges.some(edge => edge.targetHandle === handleOutputId); + const handleId = `classicalHandleOutputDynamic${node.id}-${index}`; + //const isConnected = edges.some(edge => edge.sourceHandle === handleId); + return isOutputConnected && ( + + ); + })} +
{quantumHandles.map((index, i) => { - const handleInputId = `quantumHandleInputInitialization${node.id}-${index}`; - const isInputConnected = edges.some(edge => edge.targetHandle === handleInputId); + const handleOutputId = `quantumHandleInputDynamic${node.id}-${index}`; + const isOutputConnected = edges.some(edge => edge.targetHandle === handleOutputId); const handleId = `quantumHandleOutputDynamic${node.id}-${index}`; - const isConnected = edges.some(edge => edge.sourceHandle === handleId); - return isInputConnected && ( + //const isConnected = edges.some(edge => edge.sourceHandle === handleId); + return isOutputConnected && ( diff --git a/src/components/nodes/ifelse.tsx b/src/components/nodes/ifelse.tsx index 5710f38..7fca4f4 100644 --- a/src/components/nodes/ifelse.tsx +++ b/src/components/nodes/ifelse.tsx @@ -26,13 +26,37 @@ const selector = (state: { export const IfElseNode = memo((node: Node) => { const { nodes, setNodes, updateNodeValue, setSelectedNode, edges } = useStore(selector, shallow); const updateNodeInternals = useUpdateNodeInternals(); - const [quantumHandles, setQuantumHandles] = useState([0]); - const [classicalHandles, setClassicalHandles] = useState([0]); + + const { data } = node; + const numberClassicalInputs = data.numberClassicalInputs||1; + const numberClassicalOutputs = data.numberOutputs || 0; + + const numberQuantumInputs = data.numberQuantumInputs||1; + const numberQuantumOutputs = data.numberOutputs || 0; + + //const classicalHandleCount = Math.max(numberClassicalInputs, 1); + //const quantumHandleCount = Math.max(numberQuantumInputs, 1); + //console.log(quantumHandleCount) + + const handleGap = 40; + const handleOffset = 85; + + const quantumHandles = Array.from({ length: numberQuantumInputs }, (_,i) => i); + const classicalHandles = Array.from({ length: numberClassicalInputs }, (_,i) =>i); + + //setQuantumHandles(prev => [...prev, numberQuantumInputs]); + //setClassicalHandles(prev => [...prev, numberClassicalInputs]); + //setClassicalHandles(numberClassicalInputs); + console.log("quantumHandles", quantumHandles); + console.log("classicalHandles", classicalHandles); + + const nodeHeight = Math.max(handleOffset * 1 + (numberQuantumInputs) * handleGap, 100); + const [quantumOutputHandles, setQuantumOutputHandles] = useState([0]); const [quantumOutputHandlesElse, setQuantumOutputHandlesElse] = useState([0]); - const [classicalOutputHandles, setClassicalOutputHandles] = useState([{ index: 0, branch: "then" }]); - + //const [classicalOutputHandles, setClassicalOutputHandles] = useState([{ index: 0, branch: "then" }]); + const [classicalOutputHandles, setClassicalOutputHandles] = useState([0]); const [classicalOutputHandlesElse, setClassicalOutputHandlesElse] = useState([0]); const [collapsed, setColllapsed] = useState(false); @@ -95,15 +119,42 @@ export const IfElseNode = memo((node: Node) => { } - useEffect(() => { - const connectedQuantumHandles = edges - .filter(edge => edge.target === node.id && edge.targetHandle?.startsWith(`quantumHandleInputInitialization${node.id}`)) - .map(edge => edge.targetHandle); + // quantum input handles connected to a source + const connectedQuantumHandles = quantumHandles.filter((index) => + edges.some((edge) => edge.targetHandle === `quantumHandleInputInitialization${index}${node.id}`) + ); - const connectedClassicalHandles = edges - .filter(edge => edge.target === node.id && edge.targetHandle?.startsWith(`classicalHandleInputInitialization${node.id}`)) - .map(edge => edge.targetHandle); + // use conntected output handles (quantum & classical) in each block (then & else) to determine final output handles + // each output handle in each block gets a final output handle + + // quantum output handles in then-block connected to a source + const connectedQuantumOutputHandles = quantumHandles.filter((index) => + edges.some((edge) => edge.targetHandle.startsWith(`quantumHandleDynamicOutput${index}${node.id}`)) + ); + console.log("connectedQuantumOutputHandles", connectedQuantumOutputHandles); + setQuantumOutputHandles(connectedQuantumOutputHandles); + + // quantum output handles in else-block connected to a source + const connectedOutputHandlesQuantumElse = quantumHandles.filter((index) => + edges.some((edge) => edge.targetHandle.startsWith(`quantumHandleDynamicOutputElse${index}${node.id}`)) + ); + console.log("connectedOutputHandlesQuantumElse", connectedOutputHandlesQuantumElse); + setQuantumOutputHandlesElse(connectedOutputHandlesQuantumElse); + + // classical input handles conntected to a source + const connectedClassicalHandles = classicalHandles.filter((index) => + edges.some((edge) => edge.targetHandle.startsWith(`classicalHandleInputInitialization${index}${node.id}`))); + // classical output handles in then-block connected to a source + const connectedOutputHandles = classicalHandles.filter((index) => + edges.some((edge) => edge.targetHandle?.startsWith(`classicalHandleDynamicOutput${index}${node.id}`))); + console.log("classical connectedOutputHandles", connectedOutputHandles); + setClassicalOutputHandles(connectedOutputHandles); + // classical output handles in else-block connected to a source + const connectedOutputHandlesElse = classicalHandles.filter((index) => + edges.some((edge) => edge.targetHandle?.startsWith(`classicalHandleDynamicOutputElse${index}${node.id}`))); + console.log("classical connectedOutputHandlesElse", connectedOutputHandlesElse); + setClassicalOutputHandlesElse(connectedOutputHandlesElse); const expectedHandles = quantumHandles.map(index => `quantumHandleInputInitialization${index}${node.id}`); const lastHandle = expectedHandles[expectedHandles.length - 1]; @@ -111,95 +162,70 @@ export const IfElseNode = memo((node: Node) => { const expectedClassicalHandles = classicalHandles.map(index => `classicalHandleInputInitialization${index}${node.id}`); const lastClassicalHandle = expectedClassicalHandles[expectedClassicalHandles.length - 1]; - if (connectedQuantumHandles.includes(lastHandle)) { - setQuantumHandles(prev => [...prev, prev.length]); - } - console.log(classicalHandles) - console.log(connectedClassicalHandles) - console.log(lastClassicalHandle) - - if (connectedClassicalHandles.includes(lastClassicalHandle)) { - setClassicalHandles(prev => [...prev, prev.length]); - console.log(classicalHandles) - } else { - console.log(connectedClassicalHandles) - const expectedClassicalIndices = connectedClassicalHandles.map(handleId => { - const parts = handleId.split("-"); - return parseInt(parts[parts.length - 1], 10); - }); - const nextClassicalIndex = Math.max(...expectedClassicalIndices, -1) + 1; - console.log(expectedClassicalIndices) - setClassicalHandles([...expectedClassicalIndices, nextClassicalIndex]); - - } - const connectedOutputHandles = edges - .filter(edge => edge.target === node.id && (edge.targetHandle?.startsWith(`classicalHandleDynamicOutput${node.id}`) || edge.targetHandle?.startsWith(`classicalHandleDynamicOutputElse${node.id}`))) - .map(edge => edge.targetHandle); - console.log(connectedOutputHandles) - - const expected = classicalOutputHandles.map(({ index, branch }) => { - console.log(index); - return `classicalHandleDynamicOutput${branch === "else" ? "Else" : ""}${node.id}-${index}` - } - ); - const lastHandleId = expected[expected.length - 1]; - - // Extract last branch from the last handleId - const lastBranch = lastHandleId.includes("Else") ? "else" : "then"; - - if (connectedOutputHandles.includes(lastHandleId)) { - const nextIndex = classicalOutputHandles.length; - setClassicalOutputHandles(prev => [...prev, { index: nextIndex, branch: lastBranch }]); - } - - const connectedOutputHandlesElse = edges - .filter(edge => edge.target === node.id && edge.targetHandle?.startsWith(`classicalHandleDynamicOutputElse${node.id}`)) - .map(edge => edge.targetHandle); - console.log(connectedOutputHandlesElse) - - const expectedElse = connectedClassicalHandles.map(index => `classicalHandleDynamicOutputElse${node.id}-${index}`); - const lastHandleIdElse = expectedElse[expectedElse.length - 1]; - - if (connectedOutputHandlesElse.includes(lastHandleIdElse)) { - setClassicalOutputHandlesElse(prev => [...prev, prev.length]); - } - - const connectedQuantumOutputHandles = edges - .filter(edge => edge.target === node.id && edge.targetHandle?.startsWith(`quantumHandleDynamicOutput${node.id}`)) - .map(edge => edge.targetHandle); - console.log(connectedOutputHandles) - - const quantumExpected = quantumOutputHandles.map(index => `quantumHandleDynamicOutput${index}${node.id}`); - const lastQuantumHandleId = quantumExpected[quantumExpected.length - 1]; - - if (connectedQuantumOutputHandles.includes(lastQuantumHandleId)) { - setQuantumOutputHandles(prev => [...prev, prev.length]); - } - - const connectedOutputHandlesQuantumElse = edges - .filter(edge => edge.target === node.id && edge.targetHandle?.startsWith(`quantumHandleDynamicOutputElse${node.id}`)) - .map(edge => edge.targetHandle); - console.log(connectedOutputHandlesQuantumElse) - - const expectedQuantumElse = quantumOutputHandlesElse.map(index => `quantumHandleDynamicOutputElse${index}${node.id}`); - const lastHandleIdQuantumElse = expectedQuantumElse[expectedQuantumElse.length - 1]; - - if (connectedOutputHandlesQuantumElse.includes(lastHandleIdQuantumElse)) { - setQuantumOutputHandlesElse(prev => [...prev, prev.length]); - } - + // if (connectedQuantumHandles.includes(lastHandle)) { + // setQuantumHandles(prev => [...prev, prev.length]); + // } + console.log("classicalHandles", classicalHandles) + console.log("connectedClassicalHandles", connectedClassicalHandles) + console.log("lastClassicalHandle", lastClassicalHandle) + + // if (!connectedClassicalHandles.includes(lastClassicalHandle)) { + // console.log(connectedClassicalHandles) + // const expectedClassicalIndices = connectedClassicalHandles.map(handleId => { + // const parts = handleId.split("-"); + // return parseInt(parts[parts.length - 1], 10); + // }); + // const nextClassicalIndex = Math.max(...expectedClassicalIndices, -1) + 1; + // console.log(expectedClassicalIndices) + // //setClassicalHandles([...expectedClassicalIndices, nextClassicalIndex]); + // } + + + // const expected = classicalOutputHandles.map(({ index, branch }) => { + // console.log(index); + // return `classicalHandleDynamicOutput${branch === "else" ? "Else" : ""}${node.id}-${index}` + // } + // ); + // const lastHandleId = expected[expected.length - 1]; + + // // Extract last branch from the last handleId + // const lastBranch = lastHandleId.includes("Else") ? "else" : "then"; + + // // if (connectedOutputHandles.includes(lastHandleId)) { + // // const nextIndex = classicalOutputHandles.length; + // // setClassicalOutputHandles(prev => [...prev, { index: nextIndex, branch: lastBranch }]); + // // } + + + // const expectedElse = connectedClassicalHandles.map(index => `classicalHandleDynamicOutputElse${node.id}-${index}`); + // const lastHandleIdElse = expectedElse[expectedElse.length - 1]; + + // // if (connectedOutputHandlesElse.includes(lastHandleIdElse)) { + // // setClassicalOutputHandlesElse(prev => [...prev, prev.length]); + // // } + + + + // const quantumExpected = quantumOutputHandles.map(index => `quantumHandleDynamicOutput${index}${node.id}`); + // const lastQuantumHandleId = quantumExpected[quantumExpected.length - 1]; + + // //setQuantumOutputHandles(connectedQuantumHandles); + + // const expectedQuantumElse = quantumOutputHandlesElse.map(index => `quantumHandleDynamicOutputElse${index}${node.id}`); + // const lastHandleIdQuantumElse = expectedQuantumElse[expectedQuantumElse.length - 1]; + + // if (connectedOutputHandlesQuantumElse.includes(lastHandleIdQuantumElse)) { + // setQuantumOutputHandlesElse(prev => [...prev, prev.length]); + // } updateNodeInternals(node.id); // `nodeId` is the ID of the updated node - }, [edges, node.id, quantumHandles, classicalOutputHandles, quantumOutputHandles, quantumOutputHandlesElse, classicalOutputHandlesElse]); + //}, [edges, node.id, quantumHandles, classicalOutputHandles, quantumOutputHandles, quantumOutputHandlesElse, classicalOutputHandlesElse]); + }, [edges, node.id]); const dynamicHeight = 900 + Math.max(0, quantumHandles.length - 1 + (classicalHandles.length - 1)) * 30; - const totalHandles = Math.max(classicalHandles.length + quantumHandles.length, classicalOutputHandles.length + classicalOutputHandlesElse.length + quantumOutputHandles.length + quantumOutputHandlesElse.length); + const totalHandles = Math.max(classicalHandles.length + quantumHandles.length, classicalOutputHandles.length + quantumOutputHandles.length); const hexagonHeight = Math.max(250, 280 + totalHandles * 30); const hexagonTopOffset = -(hexagonHeight / 2) + 20; - - - - return !collapsed ? (
{ backgroundColor: "#000", zIndex: 9, }} - > - + > + {/* Classical Input Then */} {classicalHandles.map((index, i) => { console.log(classicalHandles) const handleId = `classicalHandleInputInitialization${index}${node.id}`; @@ -248,10 +274,7 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`sideClassicalHandleThen${index}${node.id}`} position={Position.Right} - className={cn( - "z-10 classical-circle-port-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-black" - )} + className={"z-10 classical-circle-port-out !bg-orange-300 !border-black"} style={{ top: `${100 + i * 30}px`, overflow: "visible", @@ -264,7 +287,7 @@ export const IfElseNode = memo((node: Node) => { ); })} - + {/* Quantum Input Then */} {quantumHandles.map((index, i) => { const handleId = `quantumHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -275,10 +298,7 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`sideQuantumHandleThen${index}${node.id}`} position={Position.Right} - className={cn( - "z-10 circle-port-out", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-out !bg-blue-300 !border-black"} style={{ top: `${100 + classicalHandles.length * 30 + i * 30}px`, overflow: "visible", @@ -289,7 +309,7 @@ export const IfElseNode = memo((node: Node) => { /> ); })} - + {/* Classical Input Else */} {classicalHandles.map((handle, i) => { const handleId = `classicalHandleInputInitialization${handle}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -299,10 +319,7 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`sideClassicalHandleElse${handle}${node.id}`} position={Position.Right} - className={cn( - "z-10 classical-circle-port-hex-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-black" - )} + className={"z-10 classical-circle-port-hex-out !bg-orange-300 !border-black"} style={{ top: `calc(70% + ${30 + i * 30}px)`, left: "50%", @@ -314,7 +331,7 @@ export const IfElseNode = memo((node: Node) => { /> ); })} - + {/* Quantum Input Else */} {quantumHandles.map((index, i) => { const handleId = `quantumHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -326,10 +343,7 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`sideQuantumHandleElse${index}${node.id}`} position={Position.Right} - className={cn( - "z-10 circle-port-out", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-out !bg-blue-300 !border-black"} style={{ top: `calc(70% + ${30 * (classicalHandles.length + 1 + i)}px)`, left: "50%", @@ -401,7 +415,7 @@ export const IfElseNode = memo((node: Node) => { {/* Left handles */}
- + {/* Initial Classical Input (left polygon) */} {classicalHandles.map((index, i) => { const handleId = `classicalHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -412,10 +426,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} // check if this makes problem position={Position.Left} - className={cn( - "z-10 classical-circle-port-hex-in", - isConnected ? "!bg-orange-300 !border-black" : "!bg-orange-300 !border-black" - )} + className={"z-10 classical-circle-port-hex-in !bg-orange-300 !border-black"} style={{ top: `${hexagonTopOffset + 70 + i * 30}px`, overflow: "visible", @@ -427,7 +438,7 @@ export const IfElseNode = memo((node: Node) => { /> ); })} - + {/* Initial Quantum Input (left polygon) */} {quantumHandles.map((index, i) => { const handleId = `quantumHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -438,10 +449,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 circle-port-hex-in", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-hex-in !bg-blue-300 !border-black"} style={{ top: `${hexagonTopOffset + 100 + classicalHandles.length * 30 + i * 30}px`, @@ -467,6 +475,7 @@ export const IfElseNode = memo((node: Node) => { zIndex: 9, }} > + {/* Classical Output Then */} {classicalHandles.map((index, i) => { console.log(classicalHandles) const handleId = `classicalHandleInputInitialization${index}${node.id}`; @@ -479,10 +488,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={`classicalHandleDynamicOutput${index}${node.id}`} position={Position.Left} - className={cn( - "z-10 classical-circle-port-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-black" - )} + className={"z-10 classical-circle-port-out !bg-orange-300 !border-black" } style={{ top: `${100 + i * 30}px`, overflow: "visible", @@ -495,7 +501,7 @@ export const IfElseNode = memo((node: Node) => { ); })} - + {/* Quantum Output Then */} {quantumHandles.map((index, i) => { let handleId = `quantumHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -509,10 +515,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 circle-port-out", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-out !bg-blue-300 !border-black"} style={{ top: `${100 + classicalHandles.length * 30 + i * 30}px`, overflow: "visible", @@ -525,7 +528,7 @@ export const IfElseNode = memo((node: Node) => { })} - + {/* Classical Output Else */} {classicalHandles.map((index, i) => { let handleId = `classicalHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -538,10 +541,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 classical-circle-port-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-black" - )} + className={"z-10 classical-circle-port-out !bg-orange-300 !border-black"} style={{ top: `calc(70% + ${30 + i * 30}px)`, overflow: "visible", @@ -554,7 +554,7 @@ export const IfElseNode = memo((node: Node) => { })} - + {/* Quantum Output Else */} {quantumHandles.map((index, i) => { let handleId = `quantumHandleInputInitialization${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); @@ -566,10 +566,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 circle-port-out", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-out !bg-blue-300 !border-black"} style={{ top: `calc(70% + ${30 * (classicalHandles.length + 1 + i)}px)`, overflow: "visible", @@ -629,8 +626,8 @@ export const IfElseNode = memo((node: Node) => {
- - {classicalOutputHandles.map(({ index, branch }, i) => { + {/* Final Classical Output (right polygon)*/} + {classicalOutputHandles.map((index, i) => { console.log(classicalOutputHandles) console.log(classicalOutputHandles[i]) console.log(index) @@ -641,7 +638,7 @@ export const IfElseNode = memo((node: Node) => { const elseHandleId = `classicalHandleDynamicOutputElse${index}${node.id}`; const isElseConnected = edges.some(edge => edge.targetHandle === elseHandleId); const actualHandleId = isElseConnected ? elseHandleId : handleId; - console.log(actualHandleId) + console.log("actualHandleId", actualHandleId) return (isConnected || isElseConnected) ? ( @@ -650,50 +647,39 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`classicalHandleOutputFinal-${index}`} position={Position.Right} - className={cn( - "absolute z-10 classical-circle-port-hex-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} - style={{ top: `${i * 30}px`, overflow: "visible" }} + className={"absolute z-10 classical-circle-port-hex-out !bg-orange-300 !border-black"} + style={{ + top: `${hexagonTopOffset + 70 + i * 30}px`, + overflow: "visible" }} isConnectable={true} /> ) : null; })} - {quantumOutputHandles.map((index, i) => { + {/* Final Quantum Output (right polygon)*/} + {quantumHandles.map((index, i) => { const handleId = `quantumHandleDynamicOutput${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); - return isConnected ? ( - - ) : null; - })} - - {quantumOutputHandlesElse.map((index, i) => { - const handleId = `quantumHandleDynamicOutputElse${index}${node.id}`; - const isConnected = edges.some(edge => edge.targetHandle === handleId); + const elseHandleId = `quantumHandleDynamicOutputElse${index}${node.id}`; + const isElseConnected = edges.some(edge => edge.targetHandle === elseHandleId); + const actualHandleId = isElseConnected ? elseHandleId : handleId; + console.log("actualHandleId", actualHandleId) - return isConnected ? ( + return (isConnected || isElseConnected) ? ( ) : null; })} -
@@ -800,10 +786,7 @@ export const IfElseNode = memo((node: Node) => { type="target" id={handleId} position={Position.Left} - className={cn( - "z-10 circle-port-hex-in", - isConnected ? "!bg-blue-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"z-10 circle-port-hex-in !bg-blue-300 !border-black"} style={{ top: `${hexagonTopOffset + 100 + classicalHandles.length * 30 + i * 30}px`, @@ -814,7 +797,7 @@ export const IfElseNode = memo((node: Node) => { /> ); })} - {classicalOutputHandles.map(({ index, branch }, i) => { + {classicalOutputHandles.map((index, i) => { console.log(classicalOutputHandles) console.log(classicalOutputHandles[i]) console.log(index) @@ -834,10 +817,7 @@ export const IfElseNode = memo((node: Node) => { type="source" id={`classicalHandleOutputFinal-${index}`} position={Position.Right} - className={cn( - "absolute z-10 classical-circle-port-hex-out", - isConnected ? "!bg-orange-300 !border-black" : "!bg-gray-200 !border-dashed !border-black" - )} + className={"absolute z-10 classical-circle-port-hex-out !bg-orange-300 !border-black"} style={{ top: `${i * 30}px`, overflow: "visible" }} isConnectable={true} /> @@ -848,31 +828,18 @@ export const IfElseNode = memo((node: Node) => { const handleId = `quantumHandleDynamicOutput${index}${node.id}`; const isConnected = edges.some(edge => edge.targetHandle === handleId); - return isConnected ? ( - - ) : null; - })} - - {quantumOutputHandlesElse.map((index, i) => { - const handleId = `quantumHandleDynamicOutputElse${index}${node.id}`; - const isConnected = edges.some(edge => edge.targetHandle === handleId); + const elseHandleId = `quantumHandleDynamicOutputElse${index}${node.id}`; + const isElseConnected = edges.some(edge => edge.targetHandle === elseHandleId); + const actualHandleId = isElseConnected ? elseHandleId : handleId; - return isConnected ? ( + return (isConnected || isElseConnected) ? ( ) : null; diff --git a/src/components/nodes/merger.tsx b/src/components/nodes/merger.tsx index 7f55e35..28be47c 100644 --- a/src/components/nodes/merger.tsx +++ b/src/components/nodes/merger.tsx @@ -28,7 +28,7 @@ export const MergerNode = memo((node: Node) => { const numberOutputs = data.numberOutputs || 1; const handleCount = Math.max(numberInputs, 2); - console.log(handleCount) + console.log("handle count", handleCount) const handleGap = 40; const handleOffset = 85; @@ -50,7 +50,7 @@ export const MergerNode = memo((node: Node) => { isConnectableStart={false} /> )); - + console.log("input handles", inputHandles) const outputHandles = Array.from({ length: numberOutputs }, (_, i) => ( { useEffect(() => { updateNodeInternals(node.id); + console.log("useEffect inputHandles", inputHandles) const edgesToRemove = edges.filter((edge) => { const match = edge.targetHandle?.match( diff --git a/src/components/panels/categories.tsx b/src/components/panels/categories.tsx index 31016b1..7ba58de 100644 --- a/src/components/panels/categories.tsx +++ b/src/components/panels/categories.tsx @@ -14,7 +14,7 @@ export interface Node { } interface CategoryEntry { - description?: string; + description?: string | string[]; content: CategoryContent; } @@ -67,13 +67,13 @@ export const categories: Record = { { label: "RY(θ)", type: consts.GateNode, icon: ["RY_palette.png", "PaletteIcon_RotateY_Beginner.png"], description: "Rotates qubit around Y-axis by θ radians.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "RZ(θ)", type: consts.GateNode, icon: ["RZ_palette.png", "PaletteIcon_RotateZ_Beginner.png"], description: "Rotates qubit around Z-axis by θ radians.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "T", aliases: ["π/4 Phase"], type: consts.GateNode, icon: ["t.png", "PaletteIcon_T_Beginner.png"], description: "T gate: a π/4 phase shift.", completionGuaranteed: true, compactOptions: [true, false] }, + { label: "TDG", aliases: ["T†", "T-dagger"], type: consts.GateNode, icon: ["TDG_palette.png", "PaletteIcon_TDG_Beginner.png"], description: "T† gate: inverse of the T gate.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "X", aliases: ["Pauli-X", "NOT"], type: consts.GateNode, icon: ["pauliX.png", "PaletteIcon_X_Beginner.png"], description: "Pauli-X gate: flips the state (like a NOT gate).", completionGuaranteed: true, compactOptions: [true, false] }, { label: "Y", aliases: ["Pauli-Y"], type: consts.GateNode, icon: ["pauliY.png", "PaletteIcon_Y_Beginner.png"], description: "Pauli-Y gate: flips and phases the qubit state.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "Z", aliases: ["Pauli-Z"], type: consts.GateNode, icon: ["pauliZ.png", "PaletteIcon_Z_Beginner.png"], description: "Pauli-Z gate: adds a π phase shift to |1⟩ state.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "S", aliases: ["π/2 Phase"], type: consts.GateNode, icon: ["S_palette.png", "PaletteIcon_S_Beginner.png"], description: "S gate: a π/2 phase shift.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "SX", aliases: ["√X", "square root of X"], type: consts.GateNode, icon: ["SX_palette.png", "PaletteIcon_SX_Beginner.png"], description: "Square root of X gate (√X).", completionGuaranteed: true, compactOptions: [true, false] }, { label: "SDG", aliases: ["S†", "S-dagger"], type: consts.GateNode, icon: ["SDG_palette.png", "PaletteIcon_SDG_Beginner.png"], description: "S† gate: inverse of the S gate.", completionGuaranteed: true, compactOptions: [true, false] }, - { label: "TDG", aliases: ["T†", "T-dagger"], type: consts.GateNode, icon: ["TDG_palette.png", "PaletteIcon_TDG_Beginner.png"], description: "T† gate: inverse of the T gate.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "CNOT", aliases: ["Controlled-X"], type: consts.GateNode, icon: ["CNOT_palette.png", "PaletteIcon_CNOT_Beginner.png"], description: "Controlled-X gate: flips target qubit if control is |1⟩.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "SWAP", type: consts.GateNode, icon: ["SWAP_palette.png", "PaletteIcon_SWAP_Beginner.png"], description: "Swaps the states of two qubits.", completionGuaranteed: true, compactOptions: [true, false] }, { label: "CY", aliases: ["Controlled-Y"], type: consts.GateNode, icon: ["CY_palette.png", "PaletteIcon_CY_Beginner.png"], description: "Controlled-Y gate.", completionGuaranteed: true, compactOptions: [true, false] }, @@ -92,7 +92,7 @@ export const categories: Record = { }, [consts.dataTypes]: { - description: "Classical and quantum data types such as bits, angles, qubits, and arrays used to define block inputs.", + description: ["Classical and quantum data types such as bits, angles, qubits, and arrays used to define block inputs.", "Classical data types such as bits and arrays used to define block inputs."], content: { "Classical Datatypes": [ //{ label: "angle", dataType: "angle", type: consts.DataTypeNode, icon: "PaletteIcon_Angle.png", description: "Represents a rotation angle.", completionGuaranteed: false, compactOptions: [true, false] }, @@ -112,7 +112,7 @@ export const categories: Record = { }, [consts.operator]: { - description: "Predefined quantum and classical operators, including arithmetic, bitwise, and comparison operations.", + description: ["Predefined quantum and classical operators, including arithmetic, bitwise, and comparison operations.", "Predefined classical arithematic operator."], content: { "Quantum Operators": [ { label: consts.quantumLabel + consts.arithmeticOperatorLabel, type: consts.OperatorNode, icon: ["PaletteIcon_ArithmeticOperator.png", "PaletteIcon_Ancilla_ArithmeticOperator.png"], description: "Performs quantum arithmetic (add, subtract, etc.).", completionGuaranteed: true, compactOptions: [true, false] }, diff --git a/src/components/panels/new-node.tsx b/src/components/panels/new-node.tsx index f8f82e6..8d9a814 100644 --- a/src/components/panels/new-node.tsx +++ b/src/components/panels/new-node.tsx @@ -220,6 +220,8 @@ export const AddNodePanel = () => { {Object.entries(categories).map( ([category, { content, description }]) => { const visibleNodes = filterNodeGroup(content); + const shownDescription = (category === consts.dataTypes ||category === consts.operator)? + description[completionGuaranteed?1:0] : description; if (visibleNodes.length === 0) return null; return ( @@ -243,7 +245,7 @@ export const AddNodePanel = () => {
{description && (
- {description} + {shownDescription}
)} diff --git a/src/components/panels/text.tsx b/src/components/panels/text.tsx index 302a786..e60cb52 100644 --- a/src/components/panels/text.tsx +++ b/src/components/panels/text.tsx @@ -118,7 +118,7 @@ export const TextPanel = () => { updateNodeValue(selectedNode.id, field, value); } else if (!isNaN(Number(value))) { - selectedNode.data[field] = value; + selectedNode.data[field] = Number(value); updateNodeValue(selectedNode.id, field, value); } @@ -283,10 +283,10 @@ export const TextPanel = () => { className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" > - - - - + {!completionGuaranteed &&()} + {!completionGuaranteed &&()} + {!completionGuaranteed &&()} + {!completionGuaranteed &&()} @@ -548,43 +548,84 @@ export const TextPanel = () => {
- handleNumberChange("numberInputs", e.target.value) + handleNumberChange("numberQuantumInputs", e.target.value) } className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" - placeholder="Enter numberInputs" + placeholder="Enter numberQuantumInputs" />
- handleNumberChange("numberOutputs", e.target.value) + handleNumberChange("numberClassicalInputs", e.target.value) } className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" - placeholder="Enter numberOutputs" + placeholder="Enter numberClassicalInputs" + /> +
+ + +
+ + handleNumberChange("numberQuantumOutputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberQuantumOutputs" + /> +
+ +
+ + handleNumberChange("numberClassicalOutputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberClassicalOutputs" />
{ placeholder="Enter condition" />
+ +
+ + handleNumberChange("numberClassicalInputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberClassicalInputs" + /> +
+ +
+ + handleNumberChange("numberQuantumInputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberQuantumInputs" + /> +
)} @@ -706,6 +787,46 @@ export const TextPanel = () => { placeholder="Enter condition" /> + +
+ + handleNumberChange("numberClassicalInputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberClassicalInputs" + /> +
+ +
+ + handleNumberChange("numberQuantumInputs", e.target.value) + } + className="border block w-full border-gray-300 rounded-md sm:text-sm p-2" + placeholder="Enter numberQuantumInputs" + /> +
)}