mirror of
https://github.com/hatchet-dev/hatchet.git
synced 2026-04-22 01:40:12 -05:00
5babd09679
* improve overall docs structure and things
292 lines
8.4 KiB
TypeScript
292 lines
8.4 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { brand, state, inactive, container } from "./diagram-colors";
|
|
|
|
const STAGES = [
|
|
{ id: "ingest", label: "Ingest", color: "#3392FF" },
|
|
{ id: "parse", label: "Parse", color: "#BC46DD" },
|
|
{ id: "extract", label: "Extract", color: "#EAB308" },
|
|
{ id: "validate", label: "Validate", color: "#B8D41C" },
|
|
{ id: "output", label: "Output", color: "#22C55E" },
|
|
] as const;
|
|
|
|
type StageId = (typeof STAGES)[number]["id"];
|
|
|
|
const StageIcon: React.FC<{ id: StageId; color: string; size?: number }> = ({
|
|
id,
|
|
color,
|
|
size = 16,
|
|
}) => {
|
|
const props = {
|
|
width: size,
|
|
height: size,
|
|
viewBox: "0 0 24 24",
|
|
fill: "none",
|
|
stroke: color,
|
|
strokeWidth: "2",
|
|
strokeLinecap: "round" as const,
|
|
strokeLinejoin: "round" as const,
|
|
};
|
|
|
|
switch (id) {
|
|
case "ingest":
|
|
return (
|
|
<svg {...props}>
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
<polyline points="7 10 12 15 17 10" />
|
|
<line x1="12" y1="15" x2="12" y2="3" />
|
|
</svg>
|
|
);
|
|
case "parse":
|
|
return (
|
|
<svg {...props}>
|
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
<polyline points="14 2 14 8 20 8" />
|
|
<line x1="16" y1="13" x2="8" y2="13" />
|
|
<line x1="16" y1="17" x2="8" y2="17" />
|
|
<line x1="10" y1="9" x2="8" y2="9" />
|
|
</svg>
|
|
);
|
|
case "extract":
|
|
return (
|
|
<svg {...props}>
|
|
<path d="M12 3v18" />
|
|
<path d="M8 7l4-4 4 4" />
|
|
<path d="M8 17l4 4 4-4" />
|
|
<circle cx="12" cy="12" r="3" />
|
|
</svg>
|
|
);
|
|
case "validate":
|
|
return (
|
|
<svg {...props}>
|
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
<polyline points="22 4 12 14.01 9 11.01" />
|
|
</svg>
|
|
);
|
|
case "output":
|
|
return (
|
|
<svg {...props}>
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
|
<polyline points="17 8 12 3 7 8" />
|
|
<line x1="12" y1="3" x2="12" y2="15" />
|
|
</svg>
|
|
);
|
|
}
|
|
};
|
|
|
|
const DocumentProcessingDiagram: React.FC = () => {
|
|
const [activeStage, setActiveStage] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setActiveStage((prev) => (prev + 1) % STAGES.length);
|
|
}, 1500);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const stageWidth = 64;
|
|
const gap = 12;
|
|
const totalWidth = STAGES.length * stageWidth + (STAGES.length - 1) * gap;
|
|
const startX = (480 - totalWidth) / 2;
|
|
|
|
return (
|
|
<div
|
|
className="my-6 rounded-xl border p-6"
|
|
style={{
|
|
borderColor: "rgba(51, 146, 255, 0.2)",
|
|
backgroundColor: "rgba(10, 16, 41, 0.04)",
|
|
}}
|
|
>
|
|
<svg
|
|
viewBox="0 0 480 160"
|
|
className="mx-auto w-full"
|
|
style={{ maxWidth: 550 }}
|
|
overflow="hidden"
|
|
>
|
|
{/* Connecting arrows */}
|
|
{STAGES.slice(0, -1).map((_, i) => {
|
|
const fromX = startX + i * (stageWidth + gap) + stageWidth;
|
|
const toX = startX + (i + 1) * (stageWidth + gap);
|
|
const y = 70;
|
|
const isActive = i === activeStage || i + 1 === activeStage;
|
|
|
|
return (
|
|
<g key={`arrow-${i}`}>
|
|
<line
|
|
x1={fromX + 2}
|
|
y1={y}
|
|
x2={toX - 2}
|
|
y2={y}
|
|
stroke={isActive ? STAGES[i].color : "#1C2B4A"}
|
|
strokeWidth={isActive ? 2 : 1.5}
|
|
opacity={isActive ? 1 : 0.4}
|
|
style={{ transition: "all 0.5s ease" }}
|
|
/>
|
|
<polygon
|
|
points={`${toX - 2},${y} ${toX - 8},${y - 4} ${toX - 8},${y + 4}`}
|
|
fill={isActive ? STAGES[i].color : "#1C2B4A"}
|
|
opacity={isActive ? 1 : 0.4}
|
|
style={{ transition: "all 0.5s ease" }}
|
|
/>
|
|
</g>
|
|
);
|
|
})}
|
|
|
|
{/* Stage boxes */}
|
|
{STAGES.map((stage, i) => {
|
|
const x = startX + i * (stageWidth + gap);
|
|
const y = 40;
|
|
const isActive = i === activeStage;
|
|
|
|
return (
|
|
<g key={stage.id}>
|
|
{isActive && (
|
|
<rect
|
|
x={x - 4}
|
|
y={y - 4}
|
|
width={stageWidth + 8}
|
|
height={68}
|
|
rx={14}
|
|
fill={stage.color}
|
|
opacity={0.1}
|
|
>
|
|
<animate
|
|
attributeName="opacity"
|
|
values="0.1;0.05;0.1"
|
|
dur="1.5s"
|
|
repeatCount="indefinite"
|
|
/>
|
|
</rect>
|
|
)}
|
|
<rect
|
|
x={x}
|
|
y={y}
|
|
width={stageWidth}
|
|
height={60}
|
|
rx={10}
|
|
fill={isActive ? `${stage.color}15` : "#0A1029"}
|
|
stroke={isActive ? stage.color : "#1C2B4A"}
|
|
strokeWidth={isActive ? 2 : 1}
|
|
style={{ transition: "all 0.5s ease" }}
|
|
/>
|
|
<foreignObject
|
|
x={x + stageWidth / 2 - 8}
|
|
y={y + 12}
|
|
width={16}
|
|
height={16}
|
|
>
|
|
<StageIcon
|
|
id={stage.id}
|
|
color={isActive ? stage.color : "#64748B"}
|
|
/>
|
|
</foreignObject>
|
|
<text
|
|
x={x + stageWidth / 2}
|
|
y={y + 48}
|
|
textAnchor="middle"
|
|
fontSize="10"
|
|
fontWeight={isActive ? 600 : 400}
|
|
fill={isActive ? stage.color : "#A5C5E9"}
|
|
style={{ transition: "all 0.5s ease" }}
|
|
>
|
|
{stage.label}
|
|
</text>
|
|
</g>
|
|
);
|
|
})}
|
|
|
|
{/* Per-file fanout under Parse */}
|
|
<g
|
|
opacity={activeStage === 1 ? 1 : 0.3}
|
|
style={{ transition: "opacity 0.5s ease" }}
|
|
>
|
|
<text
|
|
x={startX + 1 * (stageWidth + gap) + stageWidth / 2}
|
|
y={125}
|
|
textAnchor="middle"
|
|
fontSize="9"
|
|
fill="#BC46DD"
|
|
>
|
|
per-file fanout
|
|
</text>
|
|
<line
|
|
x1={startX + 1 * (stageWidth + gap) + stageWidth / 2}
|
|
y1={102}
|
|
x2={startX + 1 * (stageWidth + gap) + stageWidth / 2}
|
|
y2={118}
|
|
stroke="#BC46DD"
|
|
strokeWidth={1}
|
|
strokeDasharray="3 2"
|
|
/>
|
|
</g>
|
|
|
|
{/* Rate-limited under Extract */}
|
|
<g
|
|
opacity={activeStage === 2 ? 1 : 0.3}
|
|
style={{ transition: "opacity 0.5s ease" }}
|
|
>
|
|
<text
|
|
x={startX + 2 * (stageWidth + gap) + stageWidth / 2}
|
|
y={125}
|
|
textAnchor="middle"
|
|
fontSize="9"
|
|
fill="#EAB308"
|
|
>
|
|
rate-limited
|
|
</text>
|
|
<line
|
|
x1={startX + 2 * (stageWidth + gap) + stageWidth / 2}
|
|
y1={102}
|
|
x2={startX + 2 * (stageWidth + gap) + stageWidth / 2}
|
|
y2={118}
|
|
stroke="#EAB308"
|
|
strokeWidth={1}
|
|
strokeDasharray="3 2"
|
|
/>
|
|
</g>
|
|
|
|
{/* Retry indicator under Validate */}
|
|
<g
|
|
opacity={activeStage === 3 ? 1 : 0.3}
|
|
style={{ transition: "opacity 0.5s ease" }}
|
|
>
|
|
<text
|
|
x={startX + 3 * (stageWidth + gap) + stageWidth / 2}
|
|
y={125}
|
|
textAnchor="middle"
|
|
fontSize="9"
|
|
fill="#B8D41C"
|
|
>
|
|
retries on failure
|
|
</text>
|
|
<line
|
|
x1={startX + 3 * (stageWidth + gap) + stageWidth / 2}
|
|
y1={102}
|
|
x2={startX + 3 * (stageWidth + gap) + stageWidth / 2}
|
|
y2={118}
|
|
stroke="#B8D41C"
|
|
strokeWidth={1}
|
|
strokeDasharray="3 2"
|
|
/>
|
|
</g>
|
|
</svg>
|
|
|
|
<div className="mt-3 flex items-center justify-center gap-2">
|
|
{STAGES.map((stage, i) => (
|
|
<div
|
|
key={stage.id}
|
|
className="h-1.5 rounded-full"
|
|
style={{
|
|
width: i === activeStage ? 32 : 12,
|
|
backgroundColor:
|
|
i === activeStage ? stage.color : "rgba(10, 16, 41, 0.5)",
|
|
transition: "all 0.5s ease",
|
|
}}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DocumentProcessingDiagram;
|