forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathintegrations-sidebar.tsx
More file actions
156 lines (134 loc) · 5.06 KB
/
Copy pathintegrations-sidebar.tsx
File metadata and controls
156 lines (134 loc) · 5.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"use client";
import { useState, useMemo, useEffect } from "react";
import { DocsLayoutProps } from "fumadocs-ui/layouts/docs";
import Separator from "../ui/sidebar/separator";
import Page from "../ui/sidebar/page";
import Folder from "../ui/sidebar/folder";
import IntegrationSelector, {
Integration,
} from "../ui/integrations-sidebar/integration-selector";
import IntegrationSelectorSkeleton from "../ui/integrations-sidebar/skeleton";
import { OpenedFoldersProvider } from "@/lib/hooks/use-opened-folders";
import { INTEGRATION_METADATA } from "@/lib/integrations";
type Node = DocsLayoutProps["tree"]["children"][number] & {
url: string;
name?: string;
index?: { url: string };
children?: Node[];
};
const NODE_COMPONENTS = {
separator: Separator,
page: Page,
folder: Folder,
};
const IntegrationsSidebar = ({
pageTree,
}: {
pageTree: DocsLayoutProps["tree"];
}) => {
const [selectedIntegration, setSelectedIntegration] =
useState<Integration | null>(null);
const integrationPages = useMemo(() => {
if (!selectedIntegration) return [];
// Get the integration metadata to match by label
const integrationMeta = INTEGRATION_METADATA[selectedIntegration];
const integrationLabel = integrationMeta?.label;
// Integration folders might have URLs at either:
// - /{integration} (e.g., /langgraph) - landing page URL
// - /integrations/{integration} - content folder URL
const possiblePaths = [
`/${selectedIntegration}`,
`/integrations/${selectedIntegration}`,
];
// Special mappings for folder names that don't match integration labels
const FOLDER_NAME_MAPPINGS: Record<string, string> = {
AutoGen2: "ag2",
autogen2: "ag2",
};
// Helper to check if a folder matches the integration
const matchesIntegration = (folderNode: Node): boolean => {
if (folderNode.type !== "folder") return false;
// First, try to match by URL
const url = folderNode.index?.url || folderNode.url;
if (url && possiblePaths.includes(url)) {
return true;
}
// If no URL, try to match by folder name (case-insensitive)
// Match both the integration label (e.g., "LangChain") and the ID (e.g., "langgraph")
if (folderNode.name) {
const folderNameLower = folderNode.name.toLowerCase();
const labelLower = integrationLabel?.toLowerCase() || "";
const idLower = selectedIntegration.toLowerCase();
// Check special mappings first (e.g., "AutoGen2" -> "ag2")
const mappedId =
FOLDER_NAME_MAPPINGS[folderNode.name] ||
FOLDER_NAME_MAPPINGS[folderNameLower];
if (mappedId && mappedId === selectedIntegration.toLowerCase()) {
return true;
}
// Then check direct matches
if (folderNameLower === labelLower || folderNameLower === idLower) {
return true;
}
}
return false;
};
// First, try to find at top level
let integrationFolder = pageTree.children.find((node) =>
matchesIntegration(node as Node),
) as Node | undefined;
// If not found, look inside the "integrations" parent folder
if (!integrationFolder) {
const integrationsParent = pageTree.children.find((node) => {
const folderNode = node as Node;
return (
folderNode.type === "folder" &&
(folderNode.index?.url === "/integrations" ||
folderNode.name?.toLowerCase() === "integrations")
);
}) as Node | undefined;
if (integrationsParent?.children) {
integrationFolder = integrationsParent.children.find((node) =>
matchesIntegration(node as Node),
) as Node | undefined;
}
}
return integrationFolder?.children ?? [];
}, [selectedIntegration, pageTree.children]);
// Dispatch pageTree update for OpenedFoldersProvider
useEffect(() => {
if (integrationPages.length > 0) {
const event = new CustomEvent("pageTreeUpdate", {
detail: integrationPages,
});
window.dispatchEvent(event);
}
}, [integrationPages]);
return (
<OpenedFoldersProvider>
<aside
id="nd-sidebar"
className="w-full flex-col max-w-[260px] h-full border backdrop-blur-lg border-r-0 border-border bg-glass-background rounded-l-2xl pl-3 pr-3 flex"
>
<IntegrationSelector
selectedIntegration={selectedIntegration}
setSelectedIntegration={setSelectedIntegration}
/>
{selectedIntegration ? (
<ul className="flex overflow-y-auto flex-col pr-1 max-h-full custom-scrollbar">
<li className="w-full h-6" />
{integrationPages.map((page, index) => {
const Component = NODE_COMPONENTS[page.type];
const pageUrl = page.index?.url || page.url || `page-${index}`;
const key = `${page.type}-${pageUrl}`;
return <Component key={key} node={page as Node} />;
})}
</ul>
) : (
<IntegrationSelectorSkeleton />
)}
</aside>
</OpenedFoldersProvider>
);
};
export default IntegrationsSidebar;