+ {unionFind.label && (
+
+ {unionFind.label}
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/visualizations-new/index.ts b/frontend/src/components/visualizations-new/index.ts
index c670af0..21fb955 100644
--- a/frontend/src/components/visualizations-new/index.ts
+++ b/frontend/src/components/visualizations-new/index.ts
@@ -18,6 +18,8 @@ export { GridCell } from "./primitives/grid-cell";
export { DecisionNode } from "./primitives/decision-node";
export { HeapNode } from "./primitives/heap-node";
export { IntervalBar } from "./primitives/interval-bar";
+export { TrieNode } from "./primitives/trie-node";
+export { UnionFindNode } from "./primitives/union-find-node";
// Data structures
export { ArrayView } from "./data-structures/array-view";
@@ -29,6 +31,8 @@ export { GridView } from "./data-structures/grid-view";
export { DecisionTreeView } from "./data-structures/decision-tree-view";
export { HeapView } from "./data-structures/heap-view";
export { IntervalView } from "./data-structures/interval-view";
+export { TrieView } from "./data-structures/trie-view";
+export { UnionFindView } from "./data-structures/union-find-view";
// Algorithm visualizations
export { MonotonicStackVisualization } from "./algorithms/monotonic-stack";
@@ -44,3 +48,5 @@ export { HeapVisualization } from "./algorithms/heap";
export { GreedyVisualization } from "./algorithms/greedy";
export { IntervalsVisualization } from "./algorithms/intervals";
export { MatrixTraversalVisualization } from "./algorithms/matrix-traversal";
+export { TrieVisualization } from "./algorithms/trie";
+export { UnionFindVisualization } from "./algorithms/union-find";
diff --git a/frontend/src/components/visualizations-new/primitives/union-find-node.tsx b/frontend/src/components/visualizations-new/primitives/union-find-node.tsx
new file mode 100644
index 0000000..37e5771
--- /dev/null
+++ b/frontend/src/components/visualizations-new/primitives/union-find-node.tsx
@@ -0,0 +1,129 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import { cn } from '@/lib/utils';
+import type { UnionFindNodeState } from '@/lib/visualizations/types';
+
+interface UnionFindNodeProps {
+ node: UnionFindNodeState;
+ x: number;
+ y: number;
+ radius?: number;
+ className?: string;
+}
+
+const STATE_CLASSES = {
+ normal: 'fill-[var(--surface-variant)] stroke-[var(--border)]',
+ root: 'fill-[var(--primary)]/20 stroke-[var(--primary)]',
+ finding: 'fill-[var(--info)]/20 stroke-[var(--info)]',
+ compressing: 'fill-[var(--info)]/30 stroke-[var(--info)]',
+ merging: 'fill-[var(--warning)]/20 stroke-[var(--warning)]',
+ highlighted: 'fill-[var(--primary)]/30 stroke-[var(--primary)]',
+ cycle: 'fill-[var(--error)]/20 stroke-[var(--error)]',
+} as const;
+
+const TEXT_CLASSES = {
+ normal: 'fill-[var(--foreground)]',
+ root: 'fill-[var(--primary)]',
+ finding: 'fill-[var(--info)]',
+ compressing: 'fill-[var(--info)]',
+ merging: 'fill-[var(--warning)]',
+ highlighted: 'fill-[var(--primary)]',
+ cycle: 'fill-[var(--error)]',
+} as const;
+
+export function UnionFindNode({
+ node,
+ x,
+ y,
+ radius = 24,
+ className,
+}: UnionFindNodeProps) {
+ const isActive = node.state === 'finding' || node.state === 'compressing' || node.state === 'merging';
+ const isRoot = node.parentId === null;
+
+ return (
+