update canvas.html
This commit is contained in:
+89
-55
@@ -1622,7 +1622,7 @@ Organization : OptiHK Limited
|
|||||||
|
|
||||||
|
|
||||||
// Displays a category icon with cached loading and graceful failure behavior.
|
// 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(() => {
|
const [src, setSrc] = useState(() => {
|
||||||
if (!category) return undefined;
|
if (!category) return undefined;
|
||||||
const cache = fetchIcon(category);
|
const cache = fetchIcon(category);
|
||||||
@@ -1671,7 +1671,7 @@ Organization : OptiHK Limited
|
|||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
objectFit: 'fill',
|
objectFit: imgObjectFit || 'fill',
|
||||||
pointerEvents: 'none',
|
pointerEvents: 'none',
|
||||||
}}
|
}}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
@@ -1721,7 +1721,7 @@ Organization : OptiHK Limited
|
|||||||
};
|
};
|
||||||
const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
|
const componentSize = normalizeBoxSize({ box_size: data.boxSize }, DEFAULT_COMPONENT_BOX_SIZE);
|
||||||
const portHandles = useMemo(
|
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]
|
[data.ports, data.rotation, data.flip, data.flop, componentSize]
|
||||||
);
|
);
|
||||||
const portDirectionMap = useMemo(
|
const portDirectionMap = useMemo(
|
||||||
@@ -1735,17 +1735,16 @@ Organization : OptiHK Limited
|
|||||||
const iconSize = createComponentSymbolMetrics(componentSize);
|
const iconSize = createComponentSymbolMetrics(componentSize);
|
||||||
const portLabelStyle = (portHandle) => {
|
const portLabelStyle = (portHandle) => {
|
||||||
const base = { ...portHandle.style };
|
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') {
|
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') {
|
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') {
|
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 (
|
return (
|
||||||
@@ -1759,19 +1758,18 @@ Organization : OptiHK Limited
|
|||||||
<span title={data.componentName}>{data.componentName}</span>
|
<span title={data.componentName}>{data.componentName}</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div
|
||||||
position: 'relative',
|
className="component-visual-body"
|
||||||
width: componentSize.width,
|
style={{
|
||||||
transform: componentVisualTransform,
|
width: componentSize.width,
|
||||||
transformOrigin: 'center center',
|
height: visualSize.height,
|
||||||
}}>
|
minHeight: visualSize.height,
|
||||||
<div
|
overflow: 'hidden',
|
||||||
className="component-visual-body"
|
...(visualSize.height < 50 && !isAnchorElement ? { padding: '2px 4px' } : {}),
|
||||||
style={{
|
border: selected ? '2px solid var(--accent)' : '1px solid var(--border)',
|
||||||
width: componentSize.width,
|
boxShadow: selected ? '0 0 15px rgba(56, 189, 248, 0.2)' : '0 4px 6px rgba(0,0,0,0.3)',
|
||||||
height: visualSize.height,
|
transform: componentVisualTransform,
|
||||||
border: selected ? '2px solid var(--accent)' : '1px solid var(--border)',
|
transformOrigin: 'center center',
|
||||||
boxShadow: selected ? '0 0 15px rgba(56, 189, 248, 0.2)' : '0 4px 6px rgba(0,0,0,0.3)',
|
|
||||||
...(isBasicCompactComponent ? {
|
...(isBasicCompactComponent ? {
|
||||||
padding: 0,
|
padding: 0,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -1794,8 +1792,8 @@ Organization : OptiHK Limited
|
|||||||
) : (
|
) : (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '8px', minHeight: '100%' }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '8px', minHeight: '100%' }}>
|
||||||
{!data.hideIcon && data.category && (
|
{!data.hideIcon && data.category && (
|
||||||
<div style={{ width: iconSize.width, height: iconSize.height }}>
|
<div style={{ maxWidth: iconSize.width, maxHeight: iconSize.height, width: '100%', aspectRatio: `${iconSize.width}/${iconSize.height}`, overflow: 'hidden' }}>
|
||||||
<IconImg category={data.category} />
|
<IconImg category={data.category} objectFit="contain" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{!data.category && <div style={{ width: iconSize.width, height: iconSize.height, borderRadius: 4, border: '1px solid var(--border-strong)', background: 'rgba(148, 163, 184, 0.08)' }} />}
|
{!data.category && <div style={{ width: iconSize.width, height: iconSize.height, borderRadius: 4, border: '1px solid var(--border-strong)', background: 'rgba(148, 163, 184, 0.08)' }} />}
|
||||||
@@ -1803,41 +1801,40 @@ Organization : OptiHK Limited
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{
|
<div style={{
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0, left: 0,
|
top: 0, left: 0,
|
||||||
width: '100%',
|
width: componentSize.width,
|
||||||
height: '100%',
|
height: visualSize.height,
|
||||||
pointerEvents: 'none'
|
pointerEvents: 'none'
|
||||||
}}>
|
}}>
|
||||||
{portHandles.map((portHandle) => (
|
|
||||||
<React.Fragment key={portHandle.name}>
|
|
||||||
<Handle
|
|
||||||
type="source"
|
|
||||||
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
|
|
||||||
id={portHandle.name}
|
|
||||||
title={portHandle.name}
|
|
||||||
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }}
|
|
||||||
/>
|
|
||||||
<Handle
|
|
||||||
type="target"
|
|
||||||
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
|
|
||||||
id={portHandle.name}
|
|
||||||
title={portHandle.name}
|
|
||||||
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 5, pointerEvents: 'all' }}
|
|
||||||
/>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{portHandles.map((portHandle) => (
|
{portHandles.map((portHandle) => (
|
||||||
<React.Fragment key={`label-${portHandle.name}`}>
|
<React.Fragment key={portHandle.name}>
|
||||||
<span className="port-name-label" style={portLabelStyle(portHandle)} title={portHandle.name}>
|
<Handle
|
||||||
{portHandle.name}
|
type="source"
|
||||||
</span>
|
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
|
||||||
|
id={portHandle.name}
|
||||||
|
title={portHandle.name}
|
||||||
|
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 10, pointerEvents: 'all' }}
|
||||||
|
/>
|
||||||
|
<Handle
|
||||||
|
type="target"
|
||||||
|
position={handlePositionMap[portDirectionMap.get(portHandle.name) || portHandle.position]}
|
||||||
|
id={portHandle.name}
|
||||||
|
title={portHandle.name}
|
||||||
|
style={{ ...baseHandleStyle, ...portHandle.style, zIndex: 5, pointerEvents: 'all' }}
|
||||||
|
/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{portHandles.map((portHandle) => (
|
||||||
|
<React.Fragment key={`label-${portHandle.name}`}>
|
||||||
|
<span className="port-name-label" style={portLabelStyle(portHandle)} title={portHandle.name}>
|
||||||
|
{portHandle.name}
|
||||||
|
</span>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, (prevProps, nextProps) => {
|
}, (prevProps, nextProps) => {
|
||||||
@@ -5065,6 +5062,35 @@ Organization : OptiHK Limited
|
|||||||
return boxSize ? { ...node, data: { ...node.data, boxSize } } : node;
|
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 loadedProjectPage = cellPages.find(page => page.type === 'project' && page.name === currentProjectName);
|
||||||
const nonProjectPages = cellPages.filter(page => page !== loadedProjectPage);
|
const nonProjectPages = cellPages.filter(page => page !== loadedProjectPage);
|
||||||
const resolvedProjectPage = loadedProjectPage || projectPage;
|
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 () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [pages, loadComponentMetadata]);
|
}, [pages, loadComponentMetadata, reactFlowInstance]);
|
||||||
|
|
||||||
const openTabs = useMemo(() => pages.filter(page => !page.isClosed), [pages]);
|
const openTabs = useMemo(() => pages.filter(page => !page.isClosed), [pages]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user