feat: remove unused utils functions

This commit is contained in:
dextmorgn
2025-09-16 09:20:33 +02:00
parent beb121086b
commit 638b7f4109
9 changed files with 127 additions and 258 deletions

View File

@@ -14,7 +14,7 @@ The project is organized into autonomous modules:
- **flowsint-types**: Pydantic models and type definitions
- **flowsint-transforms**: Transform modules, scanning logic, and tools
- **flowsint-api**: FastAPI server, API routes, and schemas only
- **flowsint-app**: Frontend application (unchanged)
- **flowsint-app**: Frontend application
### Module dependencies

View File

@@ -26,20 +26,15 @@ def clean_context(context: List[Dict]) -> List[Dict]:
if isinstance(item, dict):
# Create a copy and remove unwanted keys
cleaned_item = item["data"].copy()
# Remove top-level keys
cleaned_item.pop("id", None)
cleaned_item.pop("sketch_id", None)
# Remove from data if it exists
if "data" in cleaned_item and isinstance(cleaned_item["data"], dict):
cleaned_item["data"].pop("sketch_id", None)
# Remove measured/dimensions
cleaned_item.pop("measured", None)
cleaned.append(cleaned_item)
print(cleaned)
return cleaned

View File

@@ -1,11 +1,11 @@
<!DOCTYPE html>
<html lang="en" theme="dark">
<head>
<!-- <script
<script
crossOrigin="anonymous"
src="//unpkg.com/react-scan/dist/auto.global.js"
>
</script> -->
</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mail-check-icon lucide-mail-check"><path d="M22 13V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v12c0 1.1.9 2 2 2h8"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/><path d="m16 19 2 2 4-4"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-at-sign-icon lucide-at-sign"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg>

Before

Width:  |  Height:  |  Size: 388 B

After

Width:  |  Height:  |  Size: 318 B

View File

@@ -22,7 +22,7 @@ import TransformNode from "./transform-node"
import TypeNode from "./type-node"
import { type TransformNodeData } from "@/types/transform"
import { FlowControls } from "./controls"
import { getDagreLayoutedElements } from "@/lib/utils"
import { getFlowDagreLayoutedElements } from "@/lib/utils"
import { toast } from "sonner"
import { SaveModal } from "./save-modal"
import { useConfirm } from "@/components/use-confirm-dialog"
@@ -302,7 +302,7 @@ const FlowEditor = memo(({ initialEdges, initialNodes, theme, flow }: FlowEditor
const onLayout = useCallback(() => {
// Wait for nodes to be measured before running layout
setTimeout(() => {
const layouted = getDagreLayoutedElements(
const layouted = getFlowDagreLayoutedElements(
nodes.map(node => ({
...node,
measured: {

View File

@@ -151,17 +151,14 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
allowLasso = false,
minimap = false
}) => {
const [currentZoom, setCurrentZoom] = useState({
k: 1,
x: 1,
y: 1
});
const [containerSize, setContainerSize] = useState({ width: 0, height: 0 });
const isLassoActive = useGraphControls(s => s.isLassoActive)
// Hover highlighting state
const [highlightNodes, setHighlightNodes] = useState<Set<string>>(new Set());
const [highlightLinks, setHighlightLinks] = useState<Set<string>>(new Set());
const [hoverNode, setHoverNode] = useState<string | null>(null);
const zoomRef = useRef({ k: 1, x: 0, y: 0 });
const [zoomState, setZoomState] = useState({ k: 1, x: 0, y: 0 });
// Store references
const graphRef = useRef<any>();
@@ -211,6 +208,11 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
}
}, [nodes, showIcons]);
const handleZoom = useCallback((zoom: any) => {
zoomRef.current = zoom;
setZoomState(zoom);
}, []);
// Optimized graph initialization callback
const initializeGraph = useCallback((graphInstance: any) => {
if (!graphInstance) return;
@@ -315,8 +317,8 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
// Memoized rendering check
const shouldUseSimpleRendering = useMemo(() =>
nodes.length > CONSTANTS.NODE_COUNT_THRESHOLD || currentZoom.k < 1.5
, [nodes.length, currentZoom]);
nodes.length > CONSTANTS.NODE_COUNT_THRESHOLD || zoomState.k < 1.5
, [nodes.length, zoomState.k]);
// Memoized graph data transformation with proper memoization dependencies
const graphData = useMemo(() => {
@@ -350,12 +352,11 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
const group = edgeGroups.get(key)!;
const groupIndex = group.indexOf(edge);
const groupSize = group.length;
const curve = groupSize > 1 ? (groupIndex - (groupSize - 1) / 2) * 0.2 : 0;
const curvature = groupSize > 1 ? (groupIndex - (groupSize - 1) / 2) * 0.2 : 0;
return {
...edge,
edgeLabel: edge.label,
curve,
curvature,
groupIndex,
groupSize
};
@@ -421,7 +422,7 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
if (!showLabels) return new Set<string>();
// Find the appropriate layer for current zoom
const currentLayer = CONSTANTS.LABEL_LAYERS.find(layer => currentZoom.k >= layer.minZoom);
const currentLayer = CONSTANTS.LABEL_LAYERS.find(layer => zoomState.k >= layer.minZoom);
if (!currentLayer) return new Set<string>();
// Sort nodes by weight (number of connections) in descending order
@@ -440,7 +441,7 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
.slice(0, currentLayer.maxNodes);
return new Set(visibleNodes.map(node => node.id));
}, [graphData.nodes, currentZoom, showLabels]);
}, [graphData.nodes, zoomState.k, showLabels]);
// Event handlers with proper memoization
const handleNodeClick = useCallback((node: any, event: MouseEvent) => {
@@ -519,7 +520,6 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
if (node) {
const weight = node.neighbors?.length || 0;
const label = node.nodeLabel || node.label || node.id;
// Position tooltip above the node using the graph's coordinate conversion
if (graphRef.current) {
try {
@@ -582,12 +582,10 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
if (link) {
// Add the hovered link
newHighlightLinks.add(`${link.source}-${link.target}`);
// Add connected nodes
newHighlightNodes.add(link.source.id);
newHighlightNodes.add(link.target.id);
}
setHoverNode(null);
setHighlightNodes(newHighlightNodes);
setHighlightLinks(newHighlightLinks);
@@ -600,7 +598,6 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
const isHighlighted = highlightNodes.has(node.id) || isSelected(node.id);
const hasAnyHighlight = highlightNodes.size > 0 || highlightLinks.size > 0;
const isHovered = hoverNode === node.id || (isCurrent(node.id));
// Draw highlight ring for highlighted nodes
if (isHighlighted) {
ctx.beginPath();
@@ -608,21 +605,17 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
ctx.fillStyle = isHovered ? GRAPH_COLORS.NODE_HIGHLIGHT_HOVER : GRAPH_COLORS.NODE_HIGHLIGHT_DEFAULT;
ctx.fill();
}
// Set node color based on highlight state
if (hasAnyHighlight) {
ctx.fillStyle = isHighlighted ? node.nodeColor : `${node.nodeColor}7D`;
} else {
ctx.fillStyle = node.nodeColor;
}
ctx.beginPath();
ctx.arc(node.x, node.y, size, 0, 2 * Math.PI);
ctx.fill();
// Early exit for simple rendering
if (shouldUseSimpleRendering) return;
// Optimized icon rendering with cached images
if (showIcons && node.nodeType) {
const cachedImage = imageCache.get(node.nodeType);
@@ -634,7 +627,6 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
}
}
}
// Optimized label rendering with layered display
if (showLabels && globalScale > 3) {
const label = truncateText(node.nodeLabel || node.label || node.id, 58);
@@ -672,7 +664,6 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
let strokeStyle: string;
let lineWidth: number;
let fillStyle: string;
if (isHighlighted) {
strokeStyle = GRAPH_COLORS.LINK_HIGHLIGHTED;
fillStyle = GRAPH_COLORS.LINK_HIGHLIGHTED;
@@ -686,10 +677,25 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
fillStyle = GRAPH_COLORS.LINK_DEFAULT;
lineWidth = CONSTANTS.LINK_WIDTH * (forceSettings.linkWidth.value / 5);
}
// Draw connection line
// Draw connection line (use quadratic curve if curvature present)
const curvature: number = link.curvature || 0;
const dx = end.x - start.x;
const dy = end.y - start.y;
const distance = Math.sqrt(dx * dx + dy * dy) || 1;
const midX = (start.x + end.x) * 0.5;
const midY = (start.y + end.y) * 0.5;
const normX = -dy / distance;
const normY = dx / distance;
const offset = curvature * distance;
const ctrlX = midX + normX * offset;
const ctrlY = midY + normY * offset;
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
if (curvature !== 0) {
ctx.quadraticCurveTo(ctrlX, ctrlY, end.x, end.y);
} else {
ctx.lineTo(end.x, end.y);
}
ctx.strokeStyle = strokeStyle;
ctx.lineWidth = lineWidth;
ctx.stroke();
@@ -697,27 +703,38 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
const arrowLength = forceSettings.linkDirectionalArrowLength.value;
if (arrowLength && arrowLength > 0) {
const arrowRelPos = forceSettings.linkDirectionalArrowRelPos.value || 1;
// Calculate arrow position along the link
let arrowX = start.x + (end.x - start.x) * arrowRelPos;
let arrowY = start.y + (end.y - start.y) * arrowRelPos;
// If arrow is at the target node (arrowRelPos = 1), offset it to be at the node's edge
if (arrowRelPos === 1) {
const dx = end.x - start.x;
const dy = end.y - start.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
// Calculate target node size (same as in renderNode function)
const targetNodeSize = (end.nodeSize || CONSTANTS.NODE_DEFAULT_SIZE) * (forceSettings.nodeSize.value / 100 + 0.4);
// Calculate offset to place arrow at node edge
const offset = targetNodeSize / distance;
arrowX = end.x - dx * offset;
arrowY = end.y - dy * offset;
// Helper to get point and tangent along straight/curved link
const bezierPoint = (t: number) => {
if (curvature === 0) {
return { x: start.x + dx * t, y: start.y + dy * t };
}
const oneMinusT = 1 - t;
return {
x: oneMinusT * oneMinusT * start.x + 2 * oneMinusT * t * ctrlX + t * t * end.x,
y: oneMinusT * oneMinusT * start.y + 2 * oneMinusT * t * ctrlY + t * t * end.y,
};
};
const bezierTangent = (t: number) => {
if (curvature === 0) {
return { x: dx, y: dy };
}
const oneMinusT = 1 - t;
return {
x: 2 * oneMinusT * (ctrlX - start.x) + 2 * t * (end.x - ctrlX),
y: 2 * oneMinusT * (ctrlY - start.y) + 2 * t * (end.y - ctrlY),
};
};
let t = arrowRelPos;
let { x: arrowX, y: arrowY } = bezierPoint(t);
if (arrowRelPos === 1) {
const tan = bezierTangent(0.99);
const tanLen = Math.hypot(tan.x, tan.y) || 1;
const targetNodeSize = (end.nodeSize || CONSTANTS.NODE_DEFAULT_SIZE) * (forceSettings.nodeSize.value / 100 + 0.4);
arrowX = end.x - (tan.x / tanLen) * targetNodeSize;
arrowY = end.y - (tan.y / tanLen) * targetNodeSize;
}
// Calculate arrow direction
const dx = end.x - start.x;
const dy = end.y - start.y;
const angle = Math.atan2(dy, dx);
const tan = bezierTangent(t);
const angle = Math.atan2(tan.y, tan.x);
// Draw arrow head
ctx.save();
ctx.translate(arrowX, arrowY);
@@ -735,12 +752,24 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
if (shouldUseSimpleRendering || !link.label) return;
// Only show labels for highlighted links when there's any highlighting
if (isHighlighted) {
// Calculate label position and angle
tempPos.x = (start.x + end.x) * 0.5;
tempPos.y = (start.y + end.y) * 0.5;
const dx = end.x - start.x;
const dy = end.y - start.y;
let textAngle = Math.atan2(dy, dx);
// Calculate label position and angle along straight/curved link
let textAngle: number;
if ((link.curvature || 0) !== 0) {
// Bezier midpoint and tangent at t=0.5
const t = 0.5;
const oneMinusT = 1 - t;
tempPos.x = oneMinusT * oneMinusT * start.x + 2 * oneMinusT * t * ctrlX + t * t * end.x;
tempPos.y = oneMinusT * oneMinusT * start.y + 2 * oneMinusT * t * ctrlY + t * t * end.y;
const tx = 2 * oneMinusT * (ctrlX - start.x) + 2 * t * (end.x - ctrlX);
const ty = 2 * oneMinusT * (ctrlY - start.y) + 2 * t * (end.y - ctrlY);
textAngle = Math.atan2(ty, tx);
} else {
tempPos.x = (start.x + end.x) * 0.5;
tempPos.y = (start.y + end.y) * 0.5;
const sdx = end.x - start.x;
const sdy = end.y - start.y;
textAngle = Math.atan2(sdy, sdx);
}
// Flip text for readability
if (textAngle > CONSTANTS.HALF_PI || textAngle < -CONSTANTS.HALF_PI) {
textAngle += textAngle > 0 ? -CONSTANTS.PI : CONSTANTS.PI;
@@ -772,7 +801,6 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
// Restart simulation when settings change (debounced)
useEffect(() => {
let settingsTimeout: number | undefined;
const restartSimulation = () => {
if (graphRef.current && isGraphReadyRef.current) {
if (settingsTimeout) clearTimeout(settingsTimeout);
@@ -781,10 +809,8 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
}, 100) as any; // Debounce settings changes
}
};
// Restart simulation when force settings change
restartSimulation();
return () => {
if (settingsTimeout) clearTimeout(settingsTimeout);
};
@@ -892,7 +918,7 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
onNodeRightClick={handleNodeRightClick}
onNodeClick={handleNodeClick}
onBackgroundClick={handleBackgroundClick}
linkCurvature={link => link.curve}
linkCurvature={link => link.curvature || 0}
nodeCanvasObject={renderNode}
onNodeDragEnd={(node => {
node.fx = node.x;
@@ -906,8 +932,8 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
warmupTicks={forceSettings.warmupTicks.value}
dagLevelDistance={forceSettings.dagLevelDistance.value}
backgroundColor={backgroundColor}
onZoom={(zoom) => setCurrentZoom(zoom)}
onZoomEnd={(zoom) => setCurrentZoom(zoom)}
onZoom={handleZoom}
onZoomEnd={handleZoom}
linkCanvasObject={renderLink}
enableNodeDrag={!shouldUseSimpleRendering}
autoPauseRedraw={true}
@@ -917,7 +943,7 @@ const GraphViewer: React.FC<GraphViewerProps> = ({
{allowLasso && isLassoActive && <Lasso nodes={graphData.nodes} graph2ScreenCoords={graph2ScreenCoords} partial={true} width={containerSize.width}
height={containerSize.height} />}
{minimap && graphData.nodes &&
<MiniMap zoomTransform={currentZoom}
<MiniMap zoomTransform={zoomState}
canvasWidth={containerSize.width}
canvasHeight={containerSize.height}
nodes={graphData.nodes as GraphNode[]} />}

View File

@@ -73,10 +73,7 @@ const InfoDialog = () => {
</p>
<ul className="list-disc list-inside space-y-1">
<li>
<strong>Small to medium datasets (1-500 nodes):</strong> Interactive <strong>React Flow</strong> (<a href="https://reactflow.dev/" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">reactflow.dev</a>) graphs with real-time node dragging, detailed tooltips, and smooth animations for precise exploration.
</li>
<li>
<strong>Medium to large datasets (1-1500 nodes):</strong> Optimized <strong>React Force Graph</strong> (<a href="https://github.com/vasturiano/react-force-graph" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">github.com/vasturiano/react-force-graph</a>) layouts with force-directed algorithms that group related entities while maintaining interactive features for focused analysis.
<strong>Small to large datasets (1-1500 nodes):</strong> Optimized <strong>React Force Graph</strong> (<a href="https://github.com/vasturiano/react-force-graph" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">github.com/vasturiano/react-force-graph</a>) layouts with force-directed algorithms that group related entities while maintaining interactive features for focused analysis.
</li>
<li>
<strong>Large datasets (1550-100,000 nodes):</strong> High-performance <strong>Cosmograph</strong> (<a href="https://cosmograph.app" target="_blank" rel="noopener noreferrer" className="text-primary hover:underline">cosmograph.app</a>) with advanced rendering techniques, allowing you to visualize complex networks without sacrificing responsiveness or browser stability.

View File

@@ -1,122 +1,13 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import Dagre from '@dagrejs/dagre';
import { type Edge, Position, type Node } from '@xyflow/react';
import * as d3 from "d3-force"
import { GraphEdge, GraphNode } from '@/types';
interface NodePosition {
x: number;
y: number;
}
interface NodeMeasured {
width: number;
height: number;
}
interface NodeInternals {
positionAbsolute: NodePosition;
}
interface FlowNode {
measured: NodeMeasured;
internals: NodeInternals;
}
interface IntersectionPoint {
x: number;
y: number;
}
import { FlowEdge, FlowNode } from "@/stores/flow-store";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export const zoomSelector = (s: { transform: number[]; }) => s.transform[2] >= 0.6;
// this helper function returns the intersection point
// of the line between the center of the intersectionNode and the target node
function getNodeIntersection(
intersectionNode: FlowNode,
targetNode: FlowNode
): IntersectionPoint {
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured;
const intersectionNodePosition = intersectionNode.internals.positionAbsolute;
const targetPosition = targetNode.internals.positionAbsolute;
const w = intersectionNodeWidth / 2;
const h = intersectionNodeHeight / 2;
const x2 = intersectionNodePosition.x + w;
const y2 = intersectionNodePosition.y + h;
const x1 = targetPosition.x + targetNode.measured.width / 2;
const y1 = targetPosition.y + targetNode.measured.height / 2;
const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h);
const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h);
const a = 1 / (Math.abs(xx1) + Math.abs(yy1));
const xx3 = a * xx1;
const yy3 = a * yy1;
const x = w * (xx3 + yy3) + x2;
const y = h * (-xx3 + yy3) + y2;
return { x, y };
}
// returns the position (top,right,bottom or right) passed node compared to the intersection point
function getEdgePosition(node: FlowNode, intersectionPoint: IntersectionPoint): Position {
const n = { ...node.internals.positionAbsolute, ...node };
const nx = Math.round(n.x);
const ny = Math.round(n.y);
const px = Math.round(intersectionPoint.x);
const py = Math.round(intersectionPoint.y);
if (px <= nx + 1) {
return Position.Left;
}
if (px >= nx + n.measured.width - 1) {
return Position.Right;
}
if (py <= ny + 1) {
return Position.Top;
}
if (py >= n.y + n.measured.height - 1) {
return Position.Bottom;
}
return Position.Top;
}
// returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
interface EdgeParams {
sx: number;
sy: number;
tx: number;
ty: number;
sourcePos: Position;
targetPos: Position;
}
export function getEdgeParams(source: FlowNode, target: FlowNode): EdgeParams {
const sourceIntersectionPoint = getNodeIntersection(source, target);
const targetIntersectionPoint = getNodeIntersection(target, source);
const sourcePos = getEdgePosition(source, sourceIntersectionPoint);
const targetPos = getEdgePosition(target, targetIntersectionPoint);
return {
sx: sourceIntersectionPoint.x,
sy: sourceIntersectionPoint.y,
tx: targetIntersectionPoint.x,
ty: targetIntersectionPoint.y,
sourcePos,
targetPos,
};
}
interface LayoutOptions {
direction?: "LR" | "TB";
strength?: number;
@@ -124,81 +15,9 @@ interface LayoutOptions {
iterations?: number;
}
export const getForceLayoutedElements = (
nodes: Node[],
edges: Edge[],
options: LayoutOptions = {
direction: "LR",
strength: -30,
distance: 10,
iterations: 300,
},
) => {
// Create a map of node IDs to indices for the simulation
const nodeMap = new Map(nodes.map((node, i) => [node.id, i]))
// Create a copy of nodes with positions for the simulation
const nodesCopy = nodes.map((node) => ({
...node,
x: node.position?.x || Math.random() * 500,
y: node.position?.y || Math.random() * 500,
width: node.measured?.width || 0,
height: node.measured?.height || 0,
}))
// Create links for the simulation using indices
const links = edges.map((edge) => ({
source: nodeMap.get(edge.source),
target: nodeMap.get(edge.target),
original: edge,
}))
// Create the simulation
const simulation = d3
.forceSimulation(nodesCopy)
.force(
"link",
d3.forceLink(links).id((d: any) => nodeMap.get(d.id)),
)
.force("charge", d3.forceManyBody().strength(options.strength || -300))
.force("center", d3.forceCenter(250, 250))
.force(
"collision",
d3.forceCollide().radius((d: any) => Math.max(d.width, d.height) / 2 + 10),
)
// If direction is horizontal, adjust forces
if (options.direction === "LR") {
simulation.force("x", d3.forceX(250).strength(0.1))
simulation.force("y", d3.forceY(250).strength(0.05))
} else {
simulation.force("x", d3.forceX(250).strength(0.05))
simulation.force("y", d3.forceY(250).strength(0.1))
}
// Run the simulation synchronously
simulation.stop()
for (let i = 0; i < (options.iterations || 300); i++) {
simulation.tick()
}
// Update node positions based on simulation results
const updatedNodes = nodesCopy.map((node) => ({
...node,
position: {
x: node.x - node.width / 2,
y: node.y - node.height / 2,
},
}))
return {
nodes: updatedNodes,
edges,
}
}
export const getDagreLayoutedElements = (nodes: GraphNode[] | any,
edges: GraphEdge[] | any,
// dagre layout function for the main graph component.
export const getDagreLayoutedElements = (nodes: GraphNode[],
edges: GraphEdge[],
options: LayoutOptions = {
direction: "TB",
strength: -300,
@@ -228,6 +47,38 @@ export const getDagreLayoutedElements = (nodes: GraphNode[] | any,
};
};
// dagre layout function for the flow component.
export const getFlowDagreLayoutedElements = (nodes: FlowNode[],
edges: FlowEdge[],
options: LayoutOptions = {
direction: "TB",
strength: -300,
distance: 10,
iterations: 300,
},) => {
const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));
g.setGraph({ rankdir: options.direction });
edges.forEach((edge) => g.setEdge(edge.source, edge.target));
nodes.forEach((node) =>
g.setNode(node.id, {
...node,
width: node.measured?.width ?? 0,
height: node.measured?.height ?? 0,
}),
);
Dagre.layout(g);
return {
nodes: nodes.map((node) => {
const position = g.node(node.id);
const x = position.x - (node.measured?.width ?? 0) / 2;
const y = position.y - (node.measured?.height ?? 0) / 2;
return { ...node, position: { x, y } };
}),
edges,
};
};
export const sanitize = (name: string) => {
return name
@@ -364,7 +215,7 @@ export function deepObjectDiff(obj1: Dictionary, obj2: Dictionary): Dictionary {
diffObject = { ...diffObject, [key]: { value, new: false, oldValue: obj1[key] ?? null, newValue: obj2[key] ?? null, identical: obj2[key] === obj1[key] } }
}
})
// We map over the obj1 key:value duos to retrieve new keys that might have disapeared
// We map over the obj1 key:value duos to retrieve keys that might have disapeared
Object.entries(obj1).forEach(([key, value]) => {
// We check for additional keys
if (!obj2.hasOwnProperty(key))

View File

@@ -2,7 +2,7 @@
PROJECT_ROOT="$(cd "$(dirname "$0")" && pwd)"
echo "🚀 Starting Flowsint Project services..."
echo "Starting Flowsint project services..."
cleanup() {
echo "🛑 Stopping all services..."