From 866bc1de18c6e50a5c874a997fc3749398947b72 Mon Sep 17 00:00:00 2001 From: xsxx03-art <你的邮箱@example.com> Date: Thu, 4 Jun 2026 19:32:28 +0800 Subject: [PATCH] update canvas.html --- frontend/canvas.html | 144 ++++++++++++++++++++++++++----------------- 1 file changed, 89 insertions(+), 55 deletions(-) diff --git a/frontend/canvas.html b/frontend/canvas.html index a33f56e..e3e3b48 100644 --- a/frontend/canvas.html +++ b/frontend/canvas.html @@ -1622,7 +1622,7 @@ Organization : OptiHK Limited // Displays a category icon with cached loading and graceful failure behavior. - const IconImg = memo(({ category, containerStyle }) => { + const IconImg = memo(({ category, containerStyle, objectFit: imgObjectFit }) => { const [src, setSrc] = useState(() => { if (!category) return undefined; const cache = fetchIcon(category); @@ -1671,7 +1671,7 @@ Organization : OptiHK Limited style={{ width: '100%', height: '100%', - objectFit: 'fill', + objectFit: imgObjectFit || 'fill', pointerEvents: 'none', }} onError={(e) => { @@ -1721,7 +1721,7 @@ Organization : OptiHK Limited }; const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE); const portHandles = useMemo( - () => buildPortHandles(data.ports, { rotation: 0, flip: Boolean(data.flip), flop: Boolean(data.flop), boxSize: componentSize }), + () => buildPortHandles(data.ports, { rotation: data.rotation || 0, flip: Boolean(data.flip), flop: Boolean(data.flop), boxSize: componentSize }), [data.ports, data.rotation, data.flip, data.flop, componentSize] ); const portDirectionMap = useMemo( @@ -1735,17 +1735,16 @@ Organization : OptiHK Limited const iconSize = createComponentSymbolMetrics(componentSize); const portLabelStyle = (portHandle) => { const base = { ...portHandle.style }; - const unrotate = `rotate(${-(data.rotation || 0)}deg) scaleX(${data.flop ? -1 : 1}) scaleY(${data.flip ? -1 : 1})`; if (portHandle.position === 'left') { - return { ...base, left: 'auto', right: 'calc(100% + 8px)', transform: `translateY(-50%) ${unrotate}`, textAlign: 'right' }; + return { ...base, left: 'auto', right: 'calc(100% + 8px)', transform: 'translateY(-50%)', textAlign: 'right' }; } if (portHandle.position === 'right') { - return { ...base, left: 'calc(100% + 8px)', right: 'auto', transform: `translateY(-50%) ${unrotate}`, textAlign: 'left' }; + return { ...base, left: 'calc(100% + 8px)', right: 'auto', transform: 'translateY(-50%)', textAlign: 'left' }; } if (portHandle.position === 'top') { - return { ...base, top: 'auto', bottom: 'calc(100% + 8px)', transform: `translateX(-50%) ${unrotate}`, textAlign: 'center' }; + return { ...base, top: 'auto', bottom: 'calc(100% + 8px)', transform: 'translateX(-50%)', textAlign: 'center' }; } - return { ...base, top: 'calc(100% + 8px)', bottom: 'auto', transform: `translateX(-50%) ${unrotate}`, textAlign: 'center' }; + return { ...base, top: 'calc(100% + 8px)', bottom: 'auto', transform: 'translateX(-50%)', textAlign: 'center' }; }; return ( @@ -1759,19 +1758,18 @@ Organization : OptiHK Limited {data.componentName} )} -
-
{!data.hideIcon && data.category && ( -
- +
+
)} {!data.category &&
} @@ -1803,41 +1801,40 @@ Organization : OptiHK Limited )}
-
- {portHandles.map((portHandle) => ( - - - - - ))} -
- +
{portHandles.map((portHandle) => ( - - - {portHandle.name} - + + + ))}
+ + {portHandles.map((portHandle) => ( + + + {portHandle.name} + + + ))}
); }, (prevProps, nextProps) => { @@ -5065,6 +5062,35 @@ Organization : OptiHK Limited return boxSize ? { ...node, data: { ...node.data, boxSize } } : node; }) })); + + // Pre-fetch PDK component metadata so nodes render with correct boxSize immediately. + const allNodes = cellPages.flatMap(page => page.nodes); + const pdkNames = [...new Set(allNodes + .filter(n => n.data?.componentName && !n.data?.elementType + && !isForgeComponent(n.data.componentName) + && !isBasicComponent(n.data.componentName)) + .map(n => n.data.componentName))]; + if (pdkNames.length > 0) { + const metaResults = await Promise.all( + pdkNames.map(name => loadComponentMetadata(name).catch(() => null)) + ); + const metaMap = new Map( + pdkNames.filter((_, i) => metaResults[i]).map((name, i) => [name, metaResults[i]]) + ); + for (const page of cellPages) { + page.nodes = page.nodes.map(node => { + const metadata = metaMap.get(node.data?.componentName); + if (!metadata) return node; + const sz = normalizeBoxSize(metadata); + return { + ...node, + position: clampPositionToCanvas(node.position, page.canvasSize || DEFAULT_CANVAS_SIZE, sz), + data: { ...node.data, boxSize: sz, ports: metadata.pins || metadata.ports || {}, foundry: metadata.foundry || '', process: metadata.process || '' } + }; + }); + } + } + const loadedProjectPage = cellPages.find(page => page.type === 'project' && page.name === currentProjectName); const nonProjectPages = cellPages.filter(page => page !== loadedProjectPage); const resolvedProjectPage = loadedProjectPage || projectPage; @@ -5189,12 +5215,20 @@ Organization : OptiHK Limited }; }) }))); + + // Force React Flow to re-measure nodes whose boxSize / ports have changed. + requestAnimationFrame(() => { + const updatedIds = results.filter(r => r.metadata).map(r => r.nodeId); + if (updatedIds.length > 0 && reactFlowInstance.updateNodeInternals) { + reactFlowInstance.updateNodeInternals(updatedIds); + } + }); }); return () => { cancelled = true; }; - }, [pages, loadComponentMetadata]); + }, [pages, loadComponentMetadata, reactFlowInstance]); const openTabs = useMemo(() => pages.filter(page => !page.isClosed), [pages]);