Filterable integration index
A numbered catalog with a filter chip whose highlight glides via shared layoutId
npx shadcn@latest add https://ethankd.com/r/integration-grid.json- 001Altium DesignerEDA
Native library sync via REST
- 002KiCadEDA
Open plugin, community supported
- 003Cadence OrCAD / AllegroEDA
CIS connector in progress
- 004Siemens TeamcenterPLM
Integration via Teamcenter connectors
- 005Aras InnovatorPLM
REST-native, open schema
- 006PTC WindchillPLM
Standard REST + webhook events
- 007SAP S/4HANAERP
Material master sync via REST
- 008SnowflakeERP
Native SQL. Query directly
- 009BigQueryERP
REST export on schedule or event
- 010CursorAI
MCP server. Native tool calls
- 011Claude CodeAI
MCP server. Zero config
- 012LangChain / LlamaIndexAI
REST tool, Python SDK
- 013REST APICUSTOM
OpenAPI 3.1 spec, versioned
- 014GraphQLCUSTOM
Typed schema, introspectable
- 015SQLCUSTOM
Standard SQL, Postgres dialect
"use client";
import { useMemo, useState } from "react";
import { AnimatePresence, motion } from "motion/react";
type CatId = "EDA" | "PLM" | "ERP" | "AI" | "CUSTOM";
type Tool = { name: string; note: string; cat: CatId };
const TOOLS: Tool[] = [
{ name: "Altium Designer", note: "Native library sync via REST", cat: "EDA" },
{ name: "KiCad", note: "Open plugin, community supported", cat: "EDA" },
{ name: "Cadence OrCAD / Allegro", note: "CIS connector in progress", cat: "EDA" },
{ name: "Siemens Teamcenter", note: "Integration via Teamcenter connectors", cat: "PLM" },
{ name: "Aras Innovator", note: "REST-native, open schema", cat: "PLM" },
{ name: "PTC Windchill", note: "Standard REST + webhook events", cat: "PLM" },
{ name: "SAP S/4HANA", note: "Material master sync via REST", cat: "ERP" },
{ name: "Snowflake", note: "Native SQL. Query directly", cat: "ERP" },
{ name: "BigQuery", note: "REST export on schedule or event", cat: "ERP" },
{ name: "Cursor", note: "MCP server. Native tool calls", cat: "AI" },
{ name: "Claude Code", note: "MCP server. Zero config", cat: "AI" },
{ name: "LangChain / LlamaIndex", note: "REST tool, Python SDK", cat: "AI" },
{ name: "REST API", note: "OpenAPI 3.1 spec, versioned", cat: "CUSTOM" },
{ name: "GraphQL", note: "Typed schema, introspectable", cat: "CUSTOM" },
{ name: "SQL", note: "Standard SQL, Postgres dialect", cat: "CUSTOM" },
];
type FilterId = CatId | "ALL";
const FILTERS: { id: FilterId; label: string }[] = [
{ id: "ALL", label: "All" },
{ id: "EDA", label: "EDA" },
{ id: "PLM", label: "PLM" },
{ id: "ERP", label: "ERP" },
{ id: "AI", label: "AI" },
{ id: "CUSTOM", label: "Custom" },
];
const EASE = [0.32, 0.72, 0, 1] as const;
/**
* A numbered integration index, filterable by category. The active filter
* chip is one shared `layoutId` span sliding between buttons rather than
* each button owning its own background — that's what makes the highlight
* glide instead of jump when the filter changes.
*/
export function IntegrationGrid() {
const [filter, setFilter] = useState<FilterId>("ALL");
const filtered = useMemo(() => (filter === "ALL" ? TOOLS : TOOLS.filter((t) => t.cat === filter)), [filter]);
const counts = useMemo<Record<FilterId, number>>(() => {
const c: Record<FilterId, number> = { ALL: TOOLS.length, EDA: 0, PLM: 0, ERP: 0, AI: 0, CUSTOM: 0 };
for (const t of TOOLS) c[t.cat] += 1;
return c;
}, []);
return (
<div className="rounded-2xl bg-[#08090A] p-6 md:p-10">
<div className="mb-3 flex flex-wrap items-baseline gap-x-1 gap-y-2">
{FILTERS.map((f) => {
const active = f.id === filter;
return (
<button
key={f.id}
type="button"
onClick={() => setFilter(f.id)}
aria-pressed={active}
className={`group relative inline-flex min-h-[36px] items-center gap-1.5 rounded-md px-2.5 py-1.5 font-mono text-[11.5px] tracking-tight transition-colors duration-200 ${
active ? "text-[#1b1c1d]" : "text-white/62 hover:bg-white/5 hover:text-white"
}`}
>
{active && (
<motion.span
layoutId="oriv-integration-filter"
aria-hidden
className="absolute inset-0 rounded-md bg-[#FFC52E]"
transition={{ type: "spring", duration: 0.5, bounce: 0.15 }}
/>
)}
<span className="relative z-10">{f.label}</span>
<span className={`relative z-10 ${active ? "opacity-70" : "opacity-45"}`}>{counts[f.id]}</span>
</button>
);
})}
</div>
<ul className="border-t border-white/8">
<AnimatePresence mode="popLayout" initial={false}>
{filtered.map((t, i) => (
<motion.li
key={t.name}
layout
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.22, ease: EASE, layout: { duration: 0.32, ease: EASE } }}
className="group relative border-b border-white/8"
>
<div className="grid grid-cols-[2.75rem_minmax(0,1fr)_4rem] items-baseline gap-3 px-1 py-3 transition-colors duration-200 group-hover:bg-white/5">
<span className="font-mono text-[11px] tracking-[0.05em] text-white/40">{String(i + 1).padStart(3, "0")}</span>
<span className="text-[15px] font-medium tracking-tight text-white">{t.name}</span>
<span className="text-right font-mono text-[10px] tracking-[0.2em] text-white/40 group-hover:text-white/62">{t.cat}</span>
</div>
<p className="ml-[2.75rem] -mt-1 mb-3 pr-1 font-mono text-[11.5px] leading-snug text-white/62">{t.note}</p>
</motion.li>
))}
</AnimatePresence>
</ul>
</div>
);
}
Playground
- 001Altium DesignerEDA
Native library sync via REST
- 002KiCadEDA
Open plugin, community supported
- 003Cadence OrCAD / AllegroEDA
CIS connector in progress
- 004Siemens TeamcenterPLM
Integration via Teamcenter connectors
- 005Aras InnovatorPLM
REST-native, open schema
- 006PTC WindchillPLM
Standard REST + webhook events
- 007SAP S/4HANAERP
Material master sync via REST
- 008SnowflakeERP
Native SQL. Query directly
- 009BigQueryERP
REST export on schedule or event
- 010CursorAI
MCP server. Native tool calls
- 011Claude CodeAI
MCP server. Zero config
- 012LangChain / LlamaIndexAI
REST tool, Python SDK
- 013REST APICUSTOM
OpenAPI 3.1 spec, versioned
- 014GraphQLCUSTOM
Typed schema, introspectable
- 015SQLCUSTOM
Standard SQL, Postgres dialect