Updated with more bug revise. The login page and dashboard is also changedd

This commit is contained in:
2026-05-31 10:14:50 +08:00
parent e3f708a1a7
commit 9b4e8da796
14 changed files with 1986 additions and 2189 deletions
+173 -86
View File
@@ -1456,6 +1456,7 @@ Organization : OptiHK Limited
normalizeCanvasSize,
clampPositionToCanvas,
calculateLayoutBounds,
calculateCompositeBoxSize,
buildPortHandles,
buildElementPorts,
buildElementBoxSize,
@@ -2245,7 +2246,7 @@ Organization : OptiHK Limited
}
const dragData = JSON.stringify(
isUserCell
? { name: componentName, type: 'composite', ports: children.__ports__ || {} }
? { name: componentName, type: 'composite', ports: children.__ports__ || {}, boxSize: children.__boxSize__ }
: { name: componentName, category: componentCategory }
);
console.log("DRAG START: Sending data ->", dragData);
@@ -2379,7 +2380,7 @@ Organization : OptiHK Limited
const cellName = children.__cellName__ || compositeName;
const tree = children.tree || {};
const handleDragStart = (event) => {
const dragData = JSON.stringify({ name: cellName, type: 'composite', ports: children.__ports__ || {} });
const dragData = JSON.stringify({ name: cellName, type: 'composite', ports: children.__ports__ || {}, boxSize: children.__boxSize__ });
event.dataTransfer.setData('application/reactflow', dragData);
event.dataTransfer.effectAllowed = 'move';
};
@@ -2584,7 +2585,7 @@ Organization : OptiHK Limited
);
} else {
return (
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'composite', __name__: item.name, tree: item.tree || {}, pageId: item.pageId, __ports__: item.__ports__ || {} }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
<ProjectTreeNode key={item.name} name={item.name} children={{ __type__: 'composite', __name__: item.name, tree: item.tree || {}, pageId: item.pageId, __ports__: item.__ports__ || {}, __boxSize__: item.__boxSize__ }} onOpenComposite={onOpenComposite} onOpenProject={onOpenProject} onSelectInstance={onSelectInstance} onRenameCanvas={onRenameCanvas} onDeleteCanvas={onDeleteCanvas} />
);
}
})
@@ -2620,7 +2621,7 @@ Organization : OptiHK Limited
};
// Renders editable properties for selected nodes, ports, anchors, and routes.
const RightPanel = ({ selectedNode, selectedNodes = [], selectedEdge, selectedEdges = [], technologyManifest, projectName, width, onRenameComponent, onUpdateNode, onUpdateEdgeRoute }) => {
const RightPanel = ({ selectedNode, selectedNodes = [], selectedEdge, selectedEdges = [], technologyManifest, projectName, compositeNames = [], width, onRenameComponent, onUpdateNode, onUpdateEdgeRoute }) => {
const [componentData, setComponentData] = useState(null);
const [loading, setLoading] = useState(false);
const [enlarged, setEnlarged] = useState(null);
@@ -2629,16 +2630,54 @@ Organization : OptiHK Limited
const [localX, setLocalX] = useState('');
const [localY, setLocalY] = useState('');
const [localRotation, setLocalRotation] = useState('');
const [editingTransformField, setEditingTransformField] = useState(null);
const MIXED_VALUE = '--';
const selectedPositionNodes = useMemo(
() => (selectedNodes.length > 0 ? selectedNodes : (selectedNode ? [selectedNode] : [])).filter(node => node && node.position),
[selectedNodes, selectedNode]
);
const isMultiNodeSelection = selectedPositionNodes.length > 1;
const isPortRotationNode = useCallback((node) => (
node?.id === 'page-port' || node?.type === 'portNode' || node?.data?.elementType === 'port'
), []);
const getNodeRotationValue = useCallback((node) => (
isPortRotationNode(node) ? (node.data?.angle ?? 0) : (node.data?.rotation ?? 0)
), [isPortRotationNode]);
const getSharedNumericDisplay = useCallback((nodes, getValue) => {
if (!nodes.length) return '';
const firstValue = Number(getValue(nodes[0]));
if (!Number.isFinite(firstValue)) return MIXED_VALUE;
const sameValue = nodes.every(node => {
const nextValue = Number(getValue(node));
return Number.isFinite(nextValue) && Math.abs(nextValue - firstValue) < 0.0005;
});
return sameValue ? firstValue.toFixed(3) : MIXED_VALUE;
}, []);
const getSharedTextDisplay = useCallback((nodes, getValue) => {
if (!nodes.length) return '';
const firstValue = getValue(nodes[0]) || '';
return nodes.every(node => String(getValue(node) || '') === String(firstValue)) ? firstValue : MIXED_VALUE;
}, []);
const clearMixedInput = useCallback((event, setter) => {
if (event.currentTarget.value === MIXED_VALUE) {
setter('');
}
}, []);
const beginTransformInput = useCallback((event, field, setter) => {
setEditingTransformField(field);
clearMixedInput(event, setter);
}, [clearMixedInput]);
useEffect(() => {
const nodeId = selectedNode?.id;
if (!nodeId) {
if (!nodeId || isMultiNodeSelection) {
setComponentData(null);
setLoading(false);
return;
}
const compName = selectedNode?.data?.componentName;
if (selectedNode?.data?.elementType || isBasicComponent(compName)) {
const selectedIsComposite = selectedNode?.data?.type === 'composite' || compositeNames.includes(compName);
if (selectedNode?.data?.elementType || selectedIsComposite || isBasicComponent(compName)) {
setComponentData(null);
setLoading(false);
return;
@@ -2657,7 +2696,10 @@ Organization : OptiHK Limited
setLoading(true);
fetch(`/api/component/${encodeURIComponent(compName)}?project=${encodeURIComponent(projectName || '')}`)
.then(r => r.json())
.then(r => {
if (!r.ok) throw new Error('Component metadata not found');
return r.json();
})
.then(data => {
setComponentData({ ...data, nodeId: nodeId, componentDisplayName: selectedNode.data.componentDisplayName || data.name });
onUpdateNode(nodeId, {
@@ -2671,33 +2713,27 @@ Organization : OptiHK Limited
setLoading(false);
})
.catch(() => setLoading(false));
}, [selectedNode?.id, selectedNode?.data?.componentName, selectedNode?.data?.componentDisplayName, projectName, onUpdateNode]);
}, [selectedNode?.id, selectedNode?.data?.componentName, selectedNode?.data?.componentDisplayName, isMultiNodeSelection, compositeNames, projectName, onUpdateNode]);
useEffect(() => {
if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
setLocalY(selectedNode.position.y.toFixed(3));
const rot = selectedNode.id === 'page-port' || selectedNode.type === 'portNode' || selectedNode.data?.elementType === 'port'
? (selectedNode.data?.angle ?? 0)
: (selectedNode.data?.rotation ?? 0);
setLocalRotation(rot.toFixed(3));
if (editingTransformField) return;
if (selectedPositionNodes.length > 0) {
setLocalX(getSharedNumericDisplay(selectedPositionNodes, node => node.position.x));
setLocalY(getSharedNumericDisplay(selectedPositionNodes, node => node.position.y));
setLocalRotation(getSharedNumericDisplay(selectedPositionNodes, getNodeRotationValue));
return;
}
}, [selectedNode?.position.x, selectedNode?.position.y, selectedNode?.data?.rotation, selectedNode?.data?.angle, selectedNode?.id]);
const selectedPositionNodes = useMemo(
() => (selectedNodes.length > 0 ? selectedNodes : (selectedNode ? [selectedNode] : [])).filter(node => node && node.position),
[selectedNodes, selectedNode]
);
setLocalX('');
setLocalY('');
setLocalRotation('');
}, [selectedPositionNodes, getSharedNumericDisplay, getNodeRotationValue, editingTransformField]);
const updatePosition = useCallback((id, axis, value) => {
const val = parseFloat(value);
if (isNaN(val)) return;
if (selectedPositionNodes.length > 1 && selectedPositionNodes.some(node => node.id === id)) {
const baseNode = selectedPositionNodes.find(node => node.id === id) || selectedPositionNodes[0];
const delta = val - Number((baseNode.position && baseNode.position[axis]) || 0);
selectedPositionNodes.forEach(node => {
const currentValue = Number((node.position && node.position[axis]) || 0);
onUpdateNode(node.id, { position: { [axis]: currentValue + delta } });
onUpdateNode(node.id, { position: { [axis]: val } });
});
return;
}
@@ -2708,9 +2744,38 @@ Organization : OptiHK Limited
const val = parseFloat(value);
if (isNaN(val)) return;
const clamped = Math.min(180, Math.max(-180, val));
if (selectedPositionNodes.length > 1 && selectedPositionNodes.some(node => node.id === id)) {
selectedPositionNodes.forEach(node => {
const dataField = isPortRotationNode(node) ? { angle: clamped } : { rotation: clamped };
onUpdateNode(node.id, { data: dataField });
});
return;
}
const dataField = isPortNode || id === 'page-port' ? { angle: clamped } : { rotation: clamped };
onUpdateNode(id, { data: dataField });
}, [onUpdateNode]);
}, [onUpdateNode, selectedPositionNodes, isPortRotationNode]);
const commitTransformInput = useCallback((event, field, setter) => {
const rawValue = event.currentTarget.value;
const val = parseFloat(rawValue);
if (!isNaN(val) && selectedNode) {
if (field === 'x') {
updatePosition(selectedNode.id, 'x', val);
} else if (field === 'y') {
updatePosition(selectedNode.id, 'y', val);
} else {
updateRotation(selectedNode.id, val, isPortRotationNode(selectedNode));
}
setter(val.toFixed(3));
} else if (field === 'x') {
setter(getSharedNumericDisplay(selectedPositionNodes, node => node.position.x));
} else if (field === 'y') {
setter(getSharedNumericDisplay(selectedPositionNodes, node => node.position.y));
} else {
setter(getSharedNumericDisplay(selectedPositionNodes, getNodeRotationValue));
}
setEditingTransformField(null);
}, [selectedNode, selectedPositionNodes, updatePosition, updateRotation, isPortRotationNode, getSharedNumericDisplay, getNodeRotationValue]);
const toggleComponentTransform = useCallback((key) => {
if (!selectedNode) return;
@@ -2736,12 +2801,18 @@ Organization : OptiHK Limited
const basicMetadata = basicSelected ? getBasicComponentMetadata(selectedComponentName, selectedNode?.data?.basicArguments) : null;
const basicArguments = basicSelected ? createBasicSettings(selectedComponentName, selectedNode?.data?.basicArguments) : {};
const forgeArguments = createForgeArguments(selectedNode?.data?.forgeArguments);
const selectedIsPort = selectedNode && (selectedNode.type === 'portNode' || selectedNode.data?.elementType === 'port');
const selectedIsAnchor = selectedNode?.data?.elementType === 'anchor';
const selectedNodeBoxSize = selectedNode?.data?.componentName && !selectedNode?.data?.elementType
const selectedIsPort = !isMultiNodeSelection && selectedNode && (selectedNode.type === 'portNode' || selectedNode.data?.elementType === 'port');
const selectedIsAnchor = !isMultiNodeSelection && selectedNode?.data?.elementType === 'anchor';
const selectedNodeBoxSize = !isMultiNodeSelection && selectedNode?.data?.componentName && !selectedNode?.data?.elementType
? normalizeBoxSize({ box_size: selectedNode.data?.boxSize }, DEFAULT_COMPONENT_BOX_SIZE)
: null;
const xsections = Object.keys((technologyManifest || FALLBACK_TECHNOLOGY_MANIFEST).xsections || {});
const sharedComponentName = isMultiNodeSelection
? getSharedTextDisplay(selectedPositionNodes, node => node.data?.componentName || node.data?.elementType || node.type || '')
: '';
const sharedDisplayName = isMultiNodeSelection
? getSharedTextDisplay(selectedPositionNodes, node => node.data?.componentDisplayName || node.data?.label || node.id)
: '';
const selectedRouteEdges = selectedEdges.length > 0 ? selectedEdges : (selectedEdge ? [selectedEdge] : []);
if (selectedRouteEdges.length > 0) {
@@ -2908,19 +2979,12 @@ Organization : OptiHK Limited
<label>
<span>X</span>
<input
type="number"
type="text"
step="1"
value={localX}
onChange={(e) => setLocalX(e.target.value)}
onBlur={() => {
const val = parseFloat(localX);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'x', val);
setLocalX(val.toFixed(3));
} else if (selectedNode) {
setLocalX(selectedNode.position.x.toFixed(3));
}
}}
onFocus={(event) => beginTransformInput(event, 'x', setLocalX)}
onBlur={(event) => commitTransformInput(event, 'x', setLocalX)}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
@@ -2929,19 +2993,12 @@ Organization : OptiHK Limited
<label>
<span>Y</span>
<input
type="number"
type="text"
step="1"
value={localY}
onChange={(e) => setLocalY(e.target.value)}
onBlur={() => {
const val = parseFloat(localY);
if (!isNaN(val) && selectedNode) {
updatePosition(selectedNode.id, 'y', val);
setLocalY(val.toFixed(3));
} else if (selectedNode) {
setLocalY(selectedNode.position.y.toFixed(3));
}
}}
onFocus={(event) => beginTransformInput(event, 'y', setLocalY)}
onBlur={(event) => commitTransformInput(event, 'y', setLocalY)}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
@@ -2950,22 +3007,12 @@ Organization : OptiHK Limited
<label>
<span>Angle</span>
<input
type="number"
type="text"
step="1"
value={localRotation}
onChange={(e) => setLocalRotation(e.target.value)}
onBlur={() => {
const val = parseFloat(localRotation);
if (!isNaN(val) && selectedNode) {
updateRotation(selectedNode.id, val, selectedNode.type === 'portNode' || selectedNode.data?.elementType === 'port');
setLocalRotation(val.toFixed(3));
} else if (selectedNode) {
const rot = selectedNode.id === 'page-port' || selectedNode.type === 'portNode' || selectedNode.data?.elementType === 'port'
? (selectedNode.data?.angle ?? 0)
: (selectedNode.data?.rotation ?? 0);
setLocalRotation(rot.toFixed(3));
}
}}
onFocus={(event) => beginTransformInput(event, 'rotation', setLocalRotation)}
onBlur={(event) => commitTransformInput(event, 'rotation', setLocalRotation)}
onKeyDown={(e) => {
if (e.key === 'Enter') e.currentTarget.blur();
}}
@@ -2977,7 +3024,7 @@ Organization : OptiHK Limited
{selectedPositionNodes.length} selected
</div>
)}
{selectedNode?.data?.componentName && !selectedNode?.data?.elementType && (
{!isMultiNodeSelection && selectedNode?.data?.componentName && !selectedNode?.data?.elementType && (
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 12 }}>
<button
type="button"
@@ -3002,6 +3049,19 @@ Organization : OptiHK Limited
</div>
</div>
{isMultiNodeSelection && (
<div className="right-block" style={{ flexShrink: 0 }}>
<div className="right-block-header">Component Information</div>
<div className="right-block-body">
<div style={{ display: 'grid', gap: 8, color: 'var(--text-muted)' }}>
<div><strong style={{ color: 'var(--text-main)' }}>Selection:</strong> {selectedPositionNodes.length} components</div>
<div><strong style={{ color: 'var(--text-main)' }}>Name:</strong> {sharedDisplayName || MIXED_VALUE}</div>
<div><strong style={{ color: 'var(--text-main)' }}>Component:</strong> {sharedComponentName || MIXED_VALUE}</div>
</div>
</div>
</div>
)}
{selectedIsPort && (
<div className="right-block" style={{ flexShrink: 0 }}>
<div className="right-block-header">Port</div>
@@ -3095,7 +3155,7 @@ Organization : OptiHK Limited
</div>
)}
{selectedNode?.data?.componentName && !selectedNode?.data?.elementType && (
{!isMultiNodeSelection && selectedNode?.data?.componentName && !selectedNode?.data?.elementType && (
<div className="right-block" style={{ flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
<div className="right-block-header">Parameters</div>
<div className="right-block-body" style={{ flex: 1, overflowY: 'auto' }}>
@@ -3508,6 +3568,8 @@ Organization : OptiHK Limited
const edgeTypes = useMemo(() => ({ parallelRoute: ParallelRouteEdge }), []);
const activePage = useMemo(() => pages.find(p => p.id === activePageId) || null, [pages, activePageId]);
const compositePageNames = useMemo(() => pages.filter(page => page.type === 'composite').map(page => page.name), [pages]);
const compositePageNameSet = useMemo(() => new Set(compositePageNames), [compositePageNames]);
const currentNodes = activePage && Array.isArray(activePage.nodes) ? activePage.nodes : [];
const currentEdges = activePage && Array.isArray(activePage.edges) ? activePage.edges : [];
const activeCanvasSize = useMemo(() => normalizeCanvasSize(activePage?.canvasSize), [activePage?.canvasSize]);
@@ -3816,7 +3878,7 @@ Organization : OptiHK Limited
// Fetch metadata for a component before creating a loaded or dropped node.
const loadComponentMetadata = useCallback(async (componentName) => {
if (!componentName || isForgeComponent(componentName)) return null;
if (!componentName || isForgeComponent(componentName) || compositePageNameSet.has(componentName)) return null;
if (componentDataCacheRef.current.has(componentName)) {
return componentDataCacheRef.current.get(componentName);
}
@@ -3825,7 +3887,7 @@ Organization : OptiHK Limited
const data = await response.json();
componentDataCacheRef.current.set(componentName, data);
return data;
}, [currentProjectName]);
}, [currentProjectName, compositePageNameSet]);
// Send an auditable user action to the backend log endpoint.
const recordUserAction = useCallback((action, payload = {}) => {
@@ -4648,7 +4710,7 @@ Organization : OptiHK Limited
return category;
};
const pageFromYaml = (cellName, content, manifest) => {
const pageFromYaml = (cellName, content, manifest, knownCompositeNames = new Set()) => {
const doc = jsyaml.load(content) || {};
const usesGdsYUp = doc.coordinate_system === 'gds_y_up';
const firstPort = Array.isArray(doc.ports) ? doc.ports[0] : null;
@@ -4676,6 +4738,7 @@ Organization : OptiHK Limited
const instIsForge = isForgeComponent(compPath) || isForgeComponent(compName);
const instIsBasic = isBasicComponent(compPath) || isBasicComponent(compName);
const displayCompName = instIsForge ? FORGE_COMPONENT_LABEL : (instIsBasic ? compPath : compName);
const instIsComposite = knownCompositeNames.has(compName);
const basicMetadata = instIsBasic ? getBasicComponentMetadata(displayCompName, inst.settings) : null;
const loadedAvailableComponents = getAvailableComponentsForLoadedComponent(displayCompName);
const nodeId = `node-${Date.now()}-${Math.random().toString(36).substr(2, 5)}`;
@@ -4688,13 +4751,14 @@ Organization : OptiHK Limited
y: usesGdsYUp ? layoutToCanvasY(inst.y) : (parseFloat(inst.y) || 0),
},
data: {
label: displayCompName,
componentName: displayCompName,
category: instIsForge ? '' : findCategory(displayCompName),
label: instIsComposite ? instName : displayCompName,
componentName: instIsComposite ? compName : displayCompName,
category: instIsComposite || instIsForge ? '' : findCategory(displayCompName),
rotation: parseFloat(inst.rotation) || 0,
flip: toBooleanFlag(inst.flip ?? inst.mirror),
flop: toBooleanFlag(inst.flop),
componentDisplayName: instName,
type: instIsComposite ? 'composite' : undefined,
availableComponents: instIsForge ? [FORGE_COMPONENT_LABEL] : loadedAvailableComponents,
ports: instIsBasic ? (basicMetadata?.ports || {}) : (instIsForge ? {} : undefined),
boxSize: instIsBasic && basicMetadata ? normalizeBoxSize(basicMetadata) : undefined,
@@ -4763,7 +4827,18 @@ Organization : OptiHK Limited
const technology = data.technology || '';
setProjectTechnology(technology);
const manifest = await loadTechnologyManifest(technology);
const cellPages = (data.cells || []).map(cell => pageFromYaml(cell.name, cell.content, manifest));
const knownCompositeNames = new Set((data.cells || []).map(cell => cell.name).filter(name => name !== currentProjectName));
const parsedCellPages = (data.cells || []).map(cell => pageFromYaml(cell.name, cell.content, manifest, knownCompositeNames));
const compositeBoxSizes = new Map(parsedCellPages
.filter(page => page.type === 'composite')
.map(page => [page.name, calculateCompositeBoxSize(page)]));
const cellPages = parsedCellPages.map(page => ({
...page,
nodes: page.nodes.map(node => {
const boxSize = compositeBoxSizes.get(node.data?.componentName);
return boxSize ? { ...node, data: { ...node.data, boxSize } } : node;
})
}));
const loadedProjectPage = cellPages.find(page => page.type === 'project' && page.name === currentProjectName);
const nonProjectPages = cellPages.filter(page => page !== loadedProjectPage);
const resolvedProjectPage = loadedProjectPage || projectPage;
@@ -4810,23 +4885,26 @@ Organization : OptiHK Limited
useEffect(() => {
const compositePages = new Map(pages.filter(page => page.type === 'composite').map(page => [page.name, page]));
const portUpdates = new Map();
const compositeUpdates = new Map();
pages.forEach(page => {
page.nodes.forEach(node => {
const compPage = compositePages.get(node.data?.componentName);
if (!compPage) return;
const nextPorts = buildPageComponentPorts(compPage.port, compPage.nodes);
if (JSON.stringify(node.data?.ports || {}) !== JSON.stringify(nextPorts)) {
portUpdates.set(node.id, nextPorts);
const nextBoxSize = calculateCompositeBoxSize(compPage);
const portsChanged = JSON.stringify(node.data?.ports || {}) !== JSON.stringify(nextPorts);
const boxSizeChanged = JSON.stringify(node.data?.boxSize || {}) !== JSON.stringify(nextBoxSize);
if (portsChanged || boxSizeChanged) {
compositeUpdates.set(node.id, { ports: nextPorts, boxSize: nextBoxSize });
}
});
});
if (portUpdates.size === 0) return;
if (compositeUpdates.size === 0) return;
setPages(prev => prev.map(page => ({
...page,
nodes: page.nodes.map(node => (
portUpdates.has(node.id)
? { ...node, data: { ...node.data, ports: portUpdates.get(node.id) } }
compositeUpdates.has(node.id)
? { ...node, data: { ...node.data, ...compositeUpdates.get(node.id) } }
: node
))
})));
@@ -4837,7 +4915,7 @@ Organization : OptiHK Limited
pages.forEach(page => {
page.nodes.forEach(node => {
const componentName = node.data?.componentName;
if (node.data?.elementType || !componentName || isForgeComponent(componentName) || node.data?.type === 'composite') return;
if (node.data?.elementType || !componentName || isForgeComponent(componentName) || node.data?.type === 'composite' || compositePageNameSet.has(componentName)) return;
if (isBasicComponent(componentName)) {
if (node.data?.ports && node.data?.boxSize) return;
const metadata = getBasicComponentMetadata(componentName, node.data?.basicArguments);
@@ -5178,10 +5256,11 @@ Organization : OptiHK Limited
parsedData = { name: rawData, category: 'default' };
}
if (parsedData.type === 'standaloneComposite') {
const compositeBoxSize = normalizeBoxSize({ box_size: parsedData.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const position = clampPositionToCanvas(
reactFlowInstance.screenToFlowPosition({ x: event.clientX, y: event.clientY }),
activePage?.canvasSize || activeCanvasSize,
DEFAULT_COMPONENT_BOX_SIZE
compositeBoxSize
);
const newNode = {
id: Date.now().toString(),
@@ -5194,7 +5273,8 @@ Organization : OptiHK Limited
type: 'composite',
category: null,
rotation: 0,
ports: parsedData.ports || {}
ports: parsedData.ports || {},
boxSize: compositeBoxSize
}
};
setPages(prev => prev.map(p => {
@@ -5227,10 +5307,11 @@ Organization : OptiHK Limited
addLog(`Skipped self-reference: "${parsedData.name}" cannot be placed inside itself.`);
return;
}
const compositeBoxSize = normalizeBoxSize({ box_size: parsedData.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
const position = clampPositionToCanvas(
reactFlowInstance.screenToFlowPosition({ x: event.clientX, y: event.clientY }),
activePage?.canvasSize || activeCanvasSize,
DEFAULT_COMPONENT_BOX_SIZE
compositeBoxSize
);
const newNode = {
id: Date.now().toString(),
@@ -5243,7 +5324,8 @@ Organization : OptiHK Limited
type: 'composite',
category: null,
rotation: 0,
ports: parsedData.ports || {}
ports: parsedData.ports || {},
boxSize: compositeBoxSize
}
};
setPages(prev => prev.map(p => {
@@ -5651,7 +5733,8 @@ Organization : OptiHK Limited
__cellName__: componentName,
tree: compositeTrees[componentName] || {},
pageId: compPage.id,
__ports__: buildPageComponentPorts(compPage.port, compPage.nodes)
__ports__: buildPageComponentPorts(compPage.port, compPage.nodes),
__boxSize__: calculateCompositeBoxSize(compPage)
};
}
return {
@@ -5672,7 +5755,8 @@ Organization : OptiHK Limited
__cellName__: name,
tree: compositeTrees[name] || {},
pageId: compPage ? compPage.id : name,
__ports__: compPage ? buildPageComponentPorts(compPage.port, compPage.nodes) : {}
__ports__: compPage ? buildPageComponentPorts(compPage.port, compPage.nodes) : {},
__boxSize__: compPage ? calculateCompositeBoxSize(compPage) : DEFAULT_COMPONENT_BOX_SIZE
};
});
items.push({
@@ -5688,7 +5772,8 @@ Organization : OptiHK Limited
name: name,
tree: compositeTrees[name] || {},
pageId: compPage?.id || name,
__ports__: buildPageComponentPorts(compPage?.port, compPage?.nodes)
__ports__: buildPageComponentPorts(compPage?.port, compPage?.nodes),
__boxSize__: compPage ? calculateCompositeBoxSize(compPage) : DEFAULT_COMPONENT_BOX_SIZE
});
});
return items;
@@ -5705,7 +5790,8 @@ Organization : OptiHK Limited
__name__: page.name,
__category__: 'composite',
__cell__: true,
__ports__: buildPageComponentPorts(page.port, page.nodes)
__ports__: buildPageComponentPorts(page.port, page.nodes),
__boxSize__: calculateCompositeBoxSize(page)
};
});
const basicEntries = {
@@ -6159,6 +6245,7 @@ ${bundlesBlock}`;
selectedEdges={selectedEdges}
technologyManifest={technologyManifest}
projectName={currentProjectName}
compositeNames={compositePageNames}
width={rightWidth}
onRenameComponent={renameComponent}
onUpdateNode={handleUpdateNode}