From d34c0a37f5abffec964285c3d5b5ecd66cfcb437 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Mon, 18 May 2026 17:30:20 +0200 Subject: [PATCH] feat(docs): animate ingestion flow with running dots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace static smoothstep edges in the introduction page's ingestion diagram with a custom animated edge that runs glowing cyan dots along each path, conveying the source → stage → output flow. Dot duration scales with path length and is hidden under prefers-reduced-motion. --- docs-site/components/product-mechanics.tsx | 78 ++++++++++++++++++- .../tests/product-mechanics-content.test.mjs | 4 +- 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/docs-site/components/product-mechanics.tsx b/docs-site/components/product-mechanics.tsx index b30ba4c8..1aaaa411 100644 --- a/docs-site/components/product-mechanics.tsx +++ b/docs-site/components/product-mechanics.tsx @@ -3,6 +3,9 @@ import { Background, BackgroundVariant, + BaseEdge, + type EdgeProps, + getSmoothStepPath, Handle, MarkerType, type Node, @@ -182,7 +185,7 @@ const flowEdges = [ })), ].map((edge) => ({ ...edge, - type: "smoothstep" as const, + type: "animated" as const, style: { stroke: EDGE_STROKE, strokeWidth: 1.5 }, markerEnd: { type: MarkerType.ArrowClosed, @@ -337,12 +340,75 @@ function OutputNodeView({ data }: NodeProps) { ); } +const DOT_CORE_COLOR = "#67e8f9"; +const DOT_GLOW_COLOR = "#22d3ee"; +const DOT_SPEED_PX_PER_SEC = 110; +const DOT_MIN_DURATION_SEC = 0.7; + +function AnimatedSmoothStepEdge({ + id, + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + style, + markerEnd, +}: EdgeProps) { + const [path] = getSmoothStepPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + const pathId = `mechanics-flow-${id}`; + const approxLength = + Math.abs(targetX - sourceX) + Math.abs(targetY - sourceY); + const duration = Math.max( + DOT_MIN_DURATION_SEC, + approxLength / DOT_SPEED_PX_PER_SEC, + ); + const durAttr = `${duration.toFixed(2)}s`; + const beginAttr = `-${(duration / 2).toFixed(2)}s`; + + return ( + <> + + + + + + + + + + + + + + + + + ); +} + const nodeTypes = { source: SourceNodeView, stage: StageNodeView, output: OutputNodeView, }; +const edgeTypes = { + animated: AnimatedSmoothStepEdge, +}; + export function ProductMechanics() { return (
); diff --git a/docs-site/tests/product-mechanics-content.test.mjs b/docs-site/tests/product-mechanics-content.test.mjs index 1c4bb8e9..ca2e497b 100644 --- a/docs-site/tests/product-mechanics-content.test.mjs +++ b/docs-site/tests/product-mechanics-content.test.mjs @@ -86,7 +86,9 @@ test("product mechanics component explains ingestion outputs", async () => { '"use client"', "@xyflow/react", "