mirror of
https://github.com/CopilotKit/CopilotKit.git
synced 2026-09-14 16:26:20 +08:00
docs: apply sidebar review feedback
This commit is contained in:
@@ -81,23 +81,6 @@ describe("globals.css cookbook sidebar", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("globals.css sidebar Intelligence pin", () => {
|
|
||||||
it("stays muted until hover, then fills like the header Intelligence control", () => {
|
|
||||||
const idleBlock = globalsCss.match(
|
|
||||||
/\.shell-docs-intelligence-entry \{[\s\S]*?\n\}/,
|
|
||||||
)?.[0];
|
|
||||||
expect(idleBlock).toContain("background: transparent;");
|
|
||||||
expect(idleBlock).toContain("color: var(--text-muted);");
|
|
||||||
expect(idleBlock).not.toContain("background: var(--accent);");
|
|
||||||
|
|
||||||
const hoverBlock = globalsCss.match(
|
|
||||||
/\.shell-docs-intelligence-entry:hover,\s*\.shell-docs-intelligence-entry:focus-visible \{[\s\S]*?\n\}/,
|
|
||||||
)?.[0];
|
|
||||||
expect(hoverBlock).toContain("background: var(--accent);");
|
|
||||||
expect(hoverBlock).toContain("color: #fff;");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("globals.css sidebar section folders", () => {
|
describe("globals.css sidebar section folders", () => {
|
||||||
it("styles collapsible section triggers separately from page links", () => {
|
it("styles collapsible section triggers separately from page links", () => {
|
||||||
expect(globalsCss).toContain(".shell-docs-sidebar-section-label");
|
expect(globalsCss).toContain(".shell-docs-sidebar-section-label");
|
||||||
|
|||||||
@@ -314,63 +314,6 @@
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shell-docs-intelligence-entry {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
min-height: 2.5rem;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
border-radius: var(--shell-docs-radius-control);
|
|
||||||
background: transparent;
|
|
||||||
color: var(--text-muted);
|
|
||||||
font-size: 0.8125rem;
|
|
||||||
font-weight: 500;
|
|
||||||
text-decoration: none;
|
|
||||||
transition:
|
|
||||||
background-color 150ms ease-out,
|
|
||||||
color 150ms ease-out,
|
|
||||||
transform 100ms ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry svg {
|
|
||||||
fill: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry:hover,
|
|
||||||
.shell-docs-intelligence-entry:focus-visible {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry:hover svg,
|
|
||||||
.shell-docs-intelligence-entry:focus-visible svg {
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry:focus-visible {
|
|
||||||
outline: 2px solid var(--accent);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry:active {
|
|
||||||
transform: scale(0.96);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry-active {
|
|
||||||
background: color-mix(in oklch, var(--accent) 13%, transparent);
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-intelligence-entry-active:hover,
|
|
||||||
.shell-docs-intelligence-entry-active:focus-visible {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .shell-docs-intelligence-entry-active {
|
|
||||||
background: color-mix(in oklch, var(--accent) 20%, transparent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shell-docs-mega-menu {
|
.shell-docs-mega-menu {
|
||||||
padding: 1.25rem 1.5rem;
|
padding: 1.25rem 1.5rem;
|
||||||
}
|
}
|
||||||
@@ -455,14 +398,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.shell-docs-intelligence-entry,
|
|
||||||
.shell-docs-mega-menu-link {
|
.shell-docs-mega-menu-link {
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shell-docs-intelligence-entry:active {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.shell-docs-warning-surface {
|
.shell-docs-warning-surface {
|
||||||
@@ -583,20 +521,6 @@
|
|||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shell-docs-nav-link-idle.shell-docs-nav-link-intelligence:hover,
|
|
||||||
.shell-docs-nav-link-active.shell-docs-nav-link-intelligence:hover,
|
|
||||||
.shell-docs-nav-link-intelligence:focus-visible {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .shell-docs-nav-link-idle.shell-docs-nav-link-intelligence:hover,
|
|
||||||
.dark .shell-docs-nav-link-active.shell-docs-nav-link-intelligence:hover {
|
|
||||||
background: var(--accent);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* shell-docs sidebar overrides.
|
/* shell-docs sidebar overrides.
|
||||||
*
|
*
|
||||||
* Fumadocs's DocsLayout sidebar reserves a header row with the home
|
* Fumadocs's DocsLayout sidebar reserves a header row with the home
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ vi.mock("@clerk/nextjs", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
import { BrandNav, buildDocsAuthEntryHref } from "../brand-nav";
|
import { BrandNav, buildDocsAuthEntryHref } from "../brand-nav";
|
||||||
|
import { PrimaryDocsTabs } from "../primary-docs-tabs";
|
||||||
import {
|
import {
|
||||||
buildDocsUserMenuHref,
|
buildDocsUserMenuHref,
|
||||||
DocsAuthFallbackBoundary,
|
DocsAuthFallbackBoundary,
|
||||||
@@ -70,28 +71,31 @@ const globalsCss = readFileSync(
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
|
||||||
test("BrandNav opens docs from an Explore docs mega menu", () => {
|
test("BrandNav opens Docs from a mega menu", () => {
|
||||||
expect(brandNavSource).toContain("DocsMegaMenu");
|
expect(brandNavSource).toContain("DocsMegaMenu");
|
||||||
expect(brandNavSource).not.toContain('label: "Docs"');
|
expect(brandNavSource).not.toContain('label: "Docs"');
|
||||||
expect(brandNavSource).not.toContain('href: "/"');
|
expect(brandNavSource).not.toContain('href: "/"');
|
||||||
});
|
});
|
||||||
|
|
||||||
test("BrandNav puts Intelligence next to Cookbook", () => {
|
test("BrandNav keeps Intelligence out of the primary header links", () => {
|
||||||
expect(brandNavSource).toContain('label: "Cookbook"');
|
expect(brandNavSource).toContain('label: "Cookbook"');
|
||||||
expect(brandNavSource).toContain('label: "Intelligence"');
|
expect(brandNavSource).not.toContain('label: "Intelligence"');
|
||||||
expect(brandNavSource).toContain("INTELLIGENCE_DOCS_HREF");
|
expect(brandNavSource).not.toContain("INTELLIGENCE_DOCS_HREF");
|
||||||
expect(brandNavSource.indexOf('label: "Cookbook"')).toBeLessThan(
|
});
|
||||||
brandNavSource.indexOf('label: "Intelligence"'),
|
|
||||||
);
|
test("PrimaryDocsTabs keeps Intelligence out of the mobile links", () => {
|
||||||
|
const markup = renderToStaticMarkup(<PrimaryDocsTabs />);
|
||||||
|
|
||||||
|
expect(markup).toContain(">Docs<");
|
||||||
|
expect(markup).toContain(">Reference<");
|
||||||
|
expect(markup).toContain(">Cookbook<");
|
||||||
|
expect(markup).not.toContain(">Intelligence<");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("BrandNav keeps space between the center rail and search", () => {
|
test("BrandNav keeps space between the center rail and search", () => {
|
||||||
expect(brandNavSource).toContain("grid-cols-[auto_minmax(0,1fr)_auto]");
|
expect(brandNavSource).toContain("grid-cols-[auto_minmax(0,1fr)_auto]");
|
||||||
expect(brandNavSource).toContain("gap-x-8");
|
expect(brandNavSource).toContain("gap-x-8");
|
||||||
expect(brandNavSource).toContain("pl-4");
|
expect(brandNavSource).toContain("pl-4");
|
||||||
expect(globalsCss).toContain(
|
|
||||||
".shell-docs-nav-link-idle.shell-docs-nav-link-intelligence:hover",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("BrandNav uses the docs grid desktop layout cap", () => {
|
test("BrandNav uses the docs grid desktop layout cap", () => {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ afterEach(cleanup);
|
|||||||
test("opens a five-column docs map with Intelligence featured", () => {
|
test("opens a five-column docs map with Intelligence featured", () => {
|
||||||
render(<DocsMegaMenu triggerClassName="shell-docs-nav-link-active" />);
|
render(<DocsMegaMenu triggerClassName="shell-docs-nav-link-active" />);
|
||||||
|
|
||||||
fireEvent.pointerEnter(screen.getByRole("button", { name: "Explore docs" }));
|
fireEvent.pointerEnter(screen.getByRole("button", { name: "Docs" }));
|
||||||
|
|
||||||
expect(screen.getByRole("navigation", { name: "Explore docs" })).toBeTruthy();
|
expect(screen.getByRole("navigation", { name: "Docs" })).toBeTruthy();
|
||||||
expect(screen.getByRole("heading", { name: "Start" })).toBeTruthy();
|
expect(screen.getByRole("heading", { name: "Start" })).toBeTruthy();
|
||||||
expect(screen.getByRole("heading", { name: "Build" })).toBeTruthy();
|
expect(screen.getByRole("heading", { name: "Build" })).toBeTruthy();
|
||||||
expect(screen.getByRole("heading", { name: "Connect" })).toBeTruthy();
|
expect(screen.getByRole("heading", { name: "Connect" })).toBeTruthy();
|
||||||
@@ -37,6 +37,12 @@ test("opens a five-column docs map with Intelligence featured", () => {
|
|||||||
expect(intelligence.className).toContain(
|
expect(intelligence.className).toContain(
|
||||||
"shell-docs-mega-menu-link-featured",
|
"shell-docs-mega-menu-link-featured",
|
||||||
);
|
);
|
||||||
|
expect(intelligence.querySelector("svg")?.getAttribute("viewBox")).toBe(
|
||||||
|
"0 0 24 24",
|
||||||
|
);
|
||||||
|
expect(intelligence.querySelector("svg")?.getAttribute("fill")).toBe(
|
||||||
|
"currentColor",
|
||||||
|
);
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole("link", { name: "Threads" }).getAttribute("href"),
|
screen.getByRole("link", { name: "Threads" }).getAttribute("href"),
|
||||||
).toBe("/threads");
|
).toBe("/threads");
|
||||||
|
|||||||
@@ -6,16 +6,13 @@ const layoutSource = readFileSync(
|
|||||||
"utf8",
|
"utf8",
|
||||||
);
|
);
|
||||||
|
|
||||||
test("sidebar banner puts pickers above the Intelligence pin", () => {
|
test("sidebar banner keeps the pickers above the mobile docs tabs", () => {
|
||||||
const bannerIndex = layoutSource.lastIndexOf("{banner}");
|
const bannerIndex = layoutSource.lastIndexOf("{banner}");
|
||||||
const intelligenceIndex = layoutSource.lastIndexOf(
|
|
||||||
"<SidebarIntelligenceEntry",
|
|
||||||
);
|
|
||||||
const tabsIndex = layoutSource.indexOf(
|
const tabsIndex = layoutSource.indexOf(
|
||||||
'<PrimaryDocsTabs className="shell-docs-mobile-sidebar-tabs" />',
|
'<PrimaryDocsTabs className="shell-docs-mobile-sidebar-tabs" />',
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(bannerIndex).toBeGreaterThanOrEqual(0);
|
expect(bannerIndex).toBeGreaterThanOrEqual(0);
|
||||||
expect(intelligenceIndex).toBeGreaterThan(bannerIndex);
|
expect(tabsIndex).toBeGreaterThan(bannerIndex);
|
||||||
expect(tabsIndex).toBeGreaterThan(intelligenceIndex);
|
expect(layoutSource).not.toContain("SidebarIntelligenceEntry");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
// @vitest-environment jsdom
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import { cleanup, render, screen } from "@testing-library/react";
|
|
||||||
import { afterEach, expect, test, vi } from "vitest";
|
|
||||||
|
|
||||||
const navigation = vi.hoisted(() => ({
|
|
||||||
pathname: "/",
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("next/navigation", () => ({
|
|
||||||
usePathname: () => navigation.pathname,
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock("next/link", () => ({
|
|
||||||
default: ({ children, href, ...props }: React.ComponentProps<"a">) => (
|
|
||||||
<a href={href} {...props}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
import { SidebarIntelligenceEntry } from "../sidebar-intelligence-entry";
|
|
||||||
|
|
||||||
afterEach(cleanup);
|
|
||||||
|
|
||||||
test("pins Intelligence at the top of the sidebar as a docs link", () => {
|
|
||||||
navigation.pathname = "/quickstart";
|
|
||||||
render(<SidebarIntelligenceEntry />);
|
|
||||||
|
|
||||||
const link = screen.getByRole("link", { name: "Intelligence" });
|
|
||||||
expect(link.getAttribute("href")).toBe("/intelligence/overview");
|
|
||||||
expect(link.getAttribute("aria-current")).toBeNull();
|
|
||||||
expect(link.className).toContain("shell-docs-intelligence-entry");
|
|
||||||
expect(link.className).not.toContain("shell-docs-intelligence-entry-active");
|
|
||||||
expect(link.querySelector("svg")?.getAttribute("class")).not.toContain(
|
|
||||||
"fill-current",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("marks the Intelligence pin current on Intelligence docs", () => {
|
|
||||||
navigation.pathname = "/intelligence/overview";
|
|
||||||
render(<SidebarIntelligenceEntry />);
|
|
||||||
|
|
||||||
const link = screen.getByRole("link", { name: "Intelligence" });
|
|
||||||
expect(link.getAttribute("aria-current")).toBe("page");
|
|
||||||
expect(link.className).toContain("shell-docs-intelligence-entry-active");
|
|
||||||
expect(link.querySelector("svg")?.getAttribute("class")).not.toContain(
|
|
||||||
"fill-current",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -3,18 +3,14 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { usePostHog } from "posthog-js/react";
|
import { usePostHog } from "posthog-js/react";
|
||||||
import { CalendarDays, ChefHat, Star } from "lucide-react";
|
import { CalendarDays, ChefHat } from "lucide-react";
|
||||||
import { SearchTrigger } from "./search-trigger";
|
import { SearchTrigger } from "./search-trigger";
|
||||||
import { CopilotKitMark } from "./copilotkit-mark";
|
import { CopilotKitMark } from "./copilotkit-mark";
|
||||||
import { ThemeSwitch } from "./theme-switch";
|
import { ThemeSwitch } from "./theme-switch";
|
||||||
import ConsoleIcon from "./icons/console";
|
import ConsoleIcon from "./icons/console";
|
||||||
import ExternalLinkIcon from "./icons/external-link";
|
import ExternalLinkIcon from "./icons/external-link";
|
||||||
import { DocsMegaMenu } from "./docs-mega-menu";
|
import { DocsMegaMenu } from "./docs-mega-menu";
|
||||||
import {
|
import { isDocsExplorePath } from "@/lib/docs-mega-menu";
|
||||||
INTELLIGENCE_DOCS_HREF,
|
|
||||||
isDocsExplorePath,
|
|
||||||
isIntelligenceDocsPath,
|
|
||||||
} from "@/lib/docs-mega-menu";
|
|
||||||
import {
|
import {
|
||||||
DocsPublicAuthControl,
|
DocsPublicAuthControl,
|
||||||
buildDocsAuthEntryHref,
|
buildDocsAuthEntryHref,
|
||||||
@@ -50,11 +46,6 @@ const LEFT_LINKS: LeftLink[] = [
|
|||||||
label: "Cookbook",
|
label: "Cookbook",
|
||||||
href: "/cookbook",
|
href: "/cookbook",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
icon: <Star className="w-4 h-4 text-current" />,
|
|
||||||
label: "Intelligence",
|
|
||||||
href: INTELLIGENCE_DOCS_HREF,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface BrandNavProps {
|
export interface BrandNavProps {
|
||||||
@@ -69,18 +60,15 @@ export function BrandNav(_props: BrandNavProps = {}) {
|
|||||||
const posthog = usePostHog();
|
const posthog = usePostHog();
|
||||||
const authEntryHref = useDocsAuthEntryHref();
|
const authEntryHref = useDocsAuthEntryHref();
|
||||||
|
|
||||||
// Active-route detection: Reference, Cookbook, and Intelligence each own
|
// Active-route detection: Reference and Cookbook each own their prefix.
|
||||||
// their prefix. Everything else (root and framework-scoped pages)
|
// Everything else highlights Docs.
|
||||||
// highlights Explore docs.
|
|
||||||
const firstSegment = pathname === "/" ? "/" : `/${pathname.split("/")[1]}`;
|
const firstSegment = pathname === "/" ? "/" : `/${pathname.split("/")[1]}`;
|
||||||
const activeRoute =
|
const activeRoute =
|
||||||
firstSegment === "/reference"
|
firstSegment === "/reference"
|
||||||
? "/reference"
|
? "/reference"
|
||||||
: firstSegment === "/cookbook"
|
: firstSegment === "/cookbook"
|
||||||
? "/cookbook"
|
? "/cookbook"
|
||||||
: isIntelligenceDocsPath(pathname)
|
: "/";
|
||||||
? INTELLIGENCE_DOCS_HREF
|
|
||||||
: "/";
|
|
||||||
|
|
||||||
const handleTalkToEngineersClick = () => {
|
const handleTalkToEngineersClick = () => {
|
||||||
posthog?.capture("talk_to_us_clicked", { location: "docs_nav" });
|
posthog?.capture("talk_to_us_clicked", { location: "docs_nav" });
|
||||||
@@ -122,7 +110,6 @@ export function BrandNav(_props: BrandNavProps = {}) {
|
|||||||
</li>
|
</li>
|
||||||
{LEFT_LINKS.map((link) => {
|
{LEFT_LINKS.map((link) => {
|
||||||
const isActive = activeRoute === link.href;
|
const isActive = activeRoute === link.href;
|
||||||
const isIntelligence = link.href === INTELLIGENCE_DOCS_HREF;
|
|
||||||
return (
|
return (
|
||||||
<li key={link.href} className="relative h-full group">
|
<li key={link.href} className="relative h-full group">
|
||||||
<Link
|
<Link
|
||||||
@@ -131,7 +118,7 @@ export function BrandNav(_props: BrandNavProps = {}) {
|
|||||||
isActive
|
isActive
|
||||||
? "shell-docs-nav-link-active"
|
? "shell-docs-nav-link-active"
|
||||||
: "shell-docs-nav-link-idle"
|
: "shell-docs-nav-link-idle"
|
||||||
}${isIntelligence ? " shell-docs-nav-link-intelligence" : ""}`}
|
}`}
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
<span className="[@media(width<808px)]:hidden">
|
<span className="[@media(width<808px)]:hidden">
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
export function CopilotKitIcon({ className }: { className?: string }) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
fillRule="evenodd"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
className={className}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M11.7 9.938l-.476 1.59h2.622c.07.054.139.107.207.162l.193.158h-3.162L.679 24.1l-.05.04a.16.16 0 01-.194-.246l10.43-12.285.56-1.862.276.19z" />
|
||||||
|
<path d="M20.654 19.34c.007-.068.097-.09.134-.032l.566.868.009.015c.36.918.15 3.05-1.966 3.05-3.467 0-2.813-3.513-4.423-3.513-2.221 0-1.052 4.333-4.883 4.333-1.565 0-2.875-.738-3.514-2.427l1.231-.173c.138 1.004 1.125 2.463 2.418 2.464 1.962 0 1.552-4.29 4.748-4.29 2.723.001 3.053 3.468 4.423 3.468 1.298 0 1.243-1.763 1.155-2.49a1.021 1.021 0 01-.003-.22l.105-1.053z" />
|
||||||
|
<path d="M20.19 18.118l.001.003h-.002c-.046-.083-.097-.165-.148-.248a12.717 12.717 0 00-.846-1.205 19.993 19.993 0 00-.845-.998 26.91 26.91 0 00-.865-.92 39.347 39.347 0 00-3.24-2.902h3.315l2.63 6.27z" />
|
||||||
|
<path d="M23.186 2.776c-1.244 3.158-2.765 8.818-2.823 15.28 0 .004-.002.008-.003.011 0-.006 0-.013-.003-.02l-2.62-6.25a.158.158 0 00-.052-.257l-.061-.013-2.26-5.39c.322-.103.646-.208.967-.319a47.04 47.04 0 006.302-2.72c.166-.087.296-.158.383-.205l.1-.055.025-.015.008-.004a.096.096 0 00.037-.043z" />
|
||||||
|
<path d="M17.427 11.527h-3.58c-.356-.281-.71-.557-1.063-.819-.365-.27-.729-.527-1.083-.77l.903-3.008c.818-.215 1.697-.46 2.585-.739l2.238 5.336z" />
|
||||||
|
<path d="M12.775.067a.045.045 0 01.035.003c2.8 1.528 6.037 2.213 10.33 2.575.012 0 .017.01.02.021a.094.094 0 00-.1-.006l-.008.004-.025.015-.099.053c-.086.048-.215.117-.38.205a46.846 46.846 0 01-6.276 2.709 42.38 42.38 0 01-.98.32l-1.397-3.333.05-.161a.16.16 0 00-.108-.2.157.157 0 00-.092.003L12.844.124a.092.092 0 00-.12-.049.091.091 0 00-.049.12l.943 2.248-1.316 4.376a65.208 65.208 0 01-3.36.777c-.224.045-.402.08-.522.102l-.138.026-.035.006a.128.128 0 01-.01.001l-.002.001a.093.093 0 00-.074.106c.009.05.057.083.106.074h.003l.01-.002.035-.007.139-.026c.121-.023.3-.057.524-.103a66.222 66.222 0 003.264-.75l-.818 2.722-.043-.03a26.931 26.931 0 00-1.506-.959c-.614-.36-1.17-.647-1.638-.842l-.002-.002-.037-.016a.09.09 0 01-.036-.14c2.092-2.736 3.83-5.444 4.498-7.63a.093.093 0 01.115-.06z" />
|
||||||
|
<path d="M15.118 6.021a56.31 56.31 0 01-2.452.705l1.146-3.819 1.306 3.114z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -23,16 +23,17 @@ import {
|
|||||||
Server,
|
Server,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Terminal,
|
Terminal,
|
||||||
type LucideIcon,
|
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import type { LucideIcon } from "lucide-react";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
|
import { CopilotKitIcon } from "./copilotkit-icon";
|
||||||
import {
|
import {
|
||||||
DOCS_MEGA_MENU_COLUMNS,
|
DOCS_MEGA_MENU_COLUMNS,
|
||||||
isDocsExplorePath,
|
isDocsExplorePath,
|
||||||
type MegaMenuIconName,
|
|
||||||
} from "@/lib/docs-mega-menu";
|
} from "@/lib/docs-mega-menu";
|
||||||
|
import type { MegaMenuIconName } from "@/lib/docs-mega-menu";
|
||||||
|
|
||||||
const MEGA_MENU_ICONS: Record<MegaMenuIconName, LucideIcon> = {
|
const MEGA_MENU_ICONS: Record<Exclude<MegaMenuIconName, "kite">, LucideIcon> = {
|
||||||
book: BookOpen,
|
book: BookOpen,
|
||||||
rocket: Rocket,
|
rocket: Rocket,
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
@@ -118,7 +119,7 @@ export function DocsMegaMenu({
|
|||||||
className="h-4 w-4 shrink-0 text-current"
|
className="h-4 w-4 shrink-0 text-current"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
<span>Explore docs</span>
|
<span>Docs</span>
|
||||||
<ChevronDown
|
<ChevronDown
|
||||||
className={`h-3.5 w-3.5 text-current transition-transform duration-150 ${
|
className={`h-3.5 w-3.5 text-current transition-transform duration-150 ${
|
||||||
open ? "rotate-180" : ""
|
open ? "rotate-180" : ""
|
||||||
@@ -142,14 +143,15 @@ export function DocsMegaMenu({
|
|||||||
if (openedByPointer.current) event.preventDefault();
|
if (openedByPointer.current) event.preventDefault();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<nav aria-label="Explore docs">
|
<nav aria-label="Docs">
|
||||||
<div className="shell-docs-mega-menu-grid">
|
<div className="shell-docs-mega-menu-grid">
|
||||||
{DOCS_MEGA_MENU_COLUMNS.map((column) => (
|
{DOCS_MEGA_MENU_COLUMNS.map((column) => (
|
||||||
<section key={column.title} className="min-w-0">
|
<section key={column.title} className="min-w-0">
|
||||||
<h2 className="shell-docs-mega-menu-heading">{column.title}</h2>
|
<h2 className="shell-docs-mega-menu-heading">{column.title}</h2>
|
||||||
<ul role="list" className="shell-docs-mega-menu-list">
|
<ul role="list" className="shell-docs-mega-menu-list">
|
||||||
{column.links.map((link) => {
|
{column.links.map((link) => {
|
||||||
const Icon = MEGA_MENU_ICONS[link.icon];
|
const Icon =
|
||||||
|
link.icon === "kite" ? null : MEGA_MENU_ICONS[link.icon];
|
||||||
const featured = link.featured === true;
|
const featured = link.featured === true;
|
||||||
return (
|
return (
|
||||||
<li key={link.href}>
|
<li key={link.href}>
|
||||||
@@ -162,10 +164,14 @@ export function DocsMegaMenu({
|
|||||||
: "shell-docs-mega-menu-link"
|
: "shell-docs-mega-menu-link"
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Icon
|
{Icon ? (
|
||||||
className="shell-docs-mega-menu-icon"
|
<Icon
|
||||||
aria-hidden="true"
|
className="shell-docs-mega-menu-icon"
|
||||||
/>
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<CopilotKitIcon className="shell-docs-mega-menu-icon" />
|
||||||
|
)}
|
||||||
<span className="min-w-0">
|
<span className="min-w-0">
|
||||||
<span className="block truncate font-medium">
|
<span className="block truncate font-medium">
|
||||||
{link.label}
|
{link.label}
|
||||||
|
|||||||
@@ -2,14 +2,10 @@
|
|||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
import { ChefHat, Star } from "lucide-react";
|
import { ChefHat } from "lucide-react";
|
||||||
import BookIcon from "./icons/book";
|
import BookIcon from "./icons/book";
|
||||||
import { DocsMegaMenu } from "./docs-mega-menu";
|
import { DocsMegaMenu } from "./docs-mega-menu";
|
||||||
import {
|
import { isDocsExplorePath } from "@/lib/docs-mega-menu";
|
||||||
INTELLIGENCE_DOCS_HREF,
|
|
||||||
isDocsExplorePath,
|
|
||||||
isIntelligenceDocsPath,
|
|
||||||
} from "@/lib/docs-mega-menu";
|
|
||||||
|
|
||||||
const PRIMARY_DOCS_LINKS = [
|
const PRIMARY_DOCS_LINKS = [
|
||||||
{
|
{
|
||||||
@@ -27,11 +23,6 @@ const PRIMARY_DOCS_LINKS = [
|
|||||||
label: "Cookbook",
|
label: "Cookbook",
|
||||||
icon: <ChefHat className="h-4 w-4 text-current" />,
|
icon: <ChefHat className="h-4 w-4 text-current" />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
href: INTELLIGENCE_DOCS_HREF,
|
|
||||||
label: "Intelligence",
|
|
||||||
icon: <Star className="h-4 w-4 text-current" />,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function getActiveRoute(pathname: string) {
|
function getActiveRoute(pathname: string) {
|
||||||
@@ -45,10 +36,6 @@ function getActiveRoute(pathname: string) {
|
|||||||
return "/cookbook";
|
return "/cookbook";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isIntelligenceDocsPath(pathname)) {
|
|
||||||
return INTELLIGENCE_DOCS_HREF;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,11 +74,7 @@ export function PrimaryDocsTabs({
|
|||||||
<Link
|
<Link
|
||||||
key={link.href}
|
key={link.href}
|
||||||
href={link.href}
|
href={link.href}
|
||||||
className={`${tabClassName}${
|
className={tabClassName}
|
||||||
link.href === INTELLIGENCE_DOCS_HREF
|
|
||||||
? " shell-docs-nav-link-intelligence"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
aria-current={isActive ? "page" : undefined}
|
aria-current={isActive ? "page" : undefined}
|
||||||
>
|
>
|
||||||
{link.icon}
|
{link.icon}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import GithubIcon from "./icons/github";
|
|||||||
import DiscordIcon from "./icons/discord";
|
import DiscordIcon from "./icons/discord";
|
||||||
import { MobileSidebarFooterTalk } from "./mobile-sidebar-footer-talk";
|
import { MobileSidebarFooterTalk } from "./mobile-sidebar-footer-talk";
|
||||||
import { PrimaryDocsTabs } from "./primary-docs-tabs";
|
import { PrimaryDocsTabs } from "./primary-docs-tabs";
|
||||||
import { SidebarIntelligenceEntry } from "./sidebar-intelligence-entry";
|
|
||||||
|
|
||||||
// Shared Fumadocs `DocsLayout` chrome used by every shell-docs route.
|
// Shared Fumadocs `DocsLayout` chrome used by every shell-docs route.
|
||||||
// All five callers (home overview, framework root, framework-scoped MDX
|
// All five callers (home overview, framework root, framework-scoped MDX
|
||||||
@@ -50,7 +49,6 @@ export function ShellDocsLayout({
|
|||||||
banner: (
|
banner: (
|
||||||
<div key="shell-docs-sidebar-banner" className="flex flex-col gap-2">
|
<div key="shell-docs-sidebar-banner" className="flex flex-col gap-2">
|
||||||
{banner}
|
{banner}
|
||||||
<SidebarIntelligenceEntry />
|
|
||||||
<PrimaryDocsTabs className="shell-docs-mobile-sidebar-tabs" />
|
<PrimaryDocsTabs className="shell-docs-mobile-sidebar-tabs" />
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
"use client";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
|
||||||
import { usePathname } from "next/navigation";
|
|
||||||
import { Star } from "lucide-react";
|
|
||||||
import {
|
|
||||||
INTELLIGENCE_DOCS_HREF,
|
|
||||||
isIntelligenceDocsPath,
|
|
||||||
} from "@/lib/docs-mega-menu";
|
|
||||||
|
|
||||||
export function SidebarIntelligenceEntry() {
|
|
||||||
const pathname = usePathname() ?? "/";
|
|
||||||
const active = isIntelligenceDocsPath(pathname);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={INTELLIGENCE_DOCS_HREF}
|
|
||||||
className={`shell-docs-intelligence-entry ${
|
|
||||||
active ? "shell-docs-intelligence-entry-active" : ""
|
|
||||||
}`}
|
|
||||||
aria-current={active ? "page" : undefined}
|
|
||||||
>
|
|
||||||
<Star className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
||||||
<span className="min-w-0 truncate">Intelligence</span>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
<Overview />
|
<Overview />
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
<Overview components={props.components} />
|
<Overview components={props.components} />
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
import Overview from "@/snippets/shared/intelligence/overview.mdx";
|
import Overview from "@/snippets/shared/intelligence/overview.mdx";
|
||||||
|
|
||||||
<Overview components={props.components} />
|
<Overview components={props.components} />
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"title": "Intelligence",
|
"title": "Intelligence",
|
||||||
"icon": "lucide/Star",
|
"icon": "custom/copilotkit-kite",
|
||||||
"pages": ["overview", "self-hosting"]
|
"pages": ["overview", "self-hosting"]
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
<Overview />
|
<Overview />
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
<Overview components={props.components} />
|
<Overview components={props.components} />
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
import Overview from "@/snippets/shared/intelligence/overview.mdx";
|
import Overview from "@/snippets/shared/intelligence/overview.mdx";
|
||||||
|
|
||||||
<Overview components={props.components} />
|
<Overview components={props.components} />
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence capabilities.
|
description: CopilotKit Intelligence capabilities.
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
<Overview components={props.components} />
|
<Overview components={props.components} />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: CopilotKit Intelligence
|
title: CopilotKit Intelligence
|
||||||
icon: "lucide/Star"
|
icon: "custom/copilotkit-kite"
|
||||||
description: CopilotKit Intelligence overview for CopilotKit — features, cloud-hosted and self-hosted deployment options, threads, hosted inspection, and production operations.
|
description: CopilotKit Intelligence overview for CopilotKit — features, cloud-hosted and self-hosted deployment options, threads, hosted inspection, and production operations.
|
||||||
doc_type: explanation
|
doc_type: explanation
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -54,6 +54,25 @@ function pageSlugs(nodes: NavNode[]): string[] {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sectionTitles(nodes: NavNode[]): string[] {
|
||||||
|
return nodes.flatMap((node) => (node.type === "section" ? [node.title] : []));
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionNodes(nodes: NavNode[], section: string): NavNode[] {
|
||||||
|
const sectionIndex = nodes.findIndex(
|
||||||
|
(node) => node.type === "section" && node.title === section,
|
||||||
|
);
|
||||||
|
if (sectionIndex === -1) return [];
|
||||||
|
|
||||||
|
const nextSectionIndex = nodes.findIndex(
|
||||||
|
(node, index) => index > sectionIndex && node.type === "section",
|
||||||
|
);
|
||||||
|
return nodes.slice(
|
||||||
|
sectionIndex + 1,
|
||||||
|
nextSectionIndex === -1 ? undefined : nextSectionIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getReactNavTree(backendFramework: string | null): NavNode[] {
|
function getReactNavTree(backendFramework: string | null): NavNode[] {
|
||||||
if (!backendFramework) {
|
if (!backendFramework) {
|
||||||
return buildRootSurfaceNav(getDocsFolder(ROOT_FRAMEWORK));
|
return buildRootSurfaceNav(getDocsFolder(ROOT_FRAMEWORK));
|
||||||
@@ -198,6 +217,53 @@ test("maps every React navigation destination to a published Angular destination
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("uses the normalized sidebar flow for Angular docs", () => {
|
||||||
|
const navTree = getAngularDocsNavTree(null);
|
||||||
|
const titles = sectionTitles(navTree);
|
||||||
|
const gettingStarted = sectionNodes(navTree, "Getting Started");
|
||||||
|
const quickstartIndex = gettingStarted.findIndex(
|
||||||
|
(node) => node.type === "page" && node.title === "Angular quickstart",
|
||||||
|
);
|
||||||
|
const intelligenceLink = gettingStarted[quickstartIndex + 1];
|
||||||
|
const orderedSections = [
|
||||||
|
"Getting Started",
|
||||||
|
"Basics",
|
||||||
|
"Generative UI",
|
||||||
|
"App Control",
|
||||||
|
"Intelligence",
|
||||||
|
"Angular Guides",
|
||||||
|
"Built-in Agent",
|
||||||
|
"Deployment",
|
||||||
|
].filter((title) => titles.includes(title));
|
||||||
|
|
||||||
|
expect(titles.filter((title) => orderedSections.includes(title))).toEqual([
|
||||||
|
"Getting Started",
|
||||||
|
"Basics",
|
||||||
|
"Intelligence",
|
||||||
|
"Angular Guides",
|
||||||
|
"Built-in Agent",
|
||||||
|
"Deployment",
|
||||||
|
]);
|
||||||
|
expect(titles.filter((title) => title === "Getting Started")).toHaveLength(1);
|
||||||
|
expect(titles.indexOf("Angular Guides")).toBeGreaterThan(
|
||||||
|
titles.indexOf("Intelligence"),
|
||||||
|
);
|
||||||
|
expect(titles.indexOf("Deployment")).toBeGreaterThan(
|
||||||
|
titles.indexOf("Angular Guides"),
|
||||||
|
);
|
||||||
|
expect(intelligenceLink).toMatchObject({
|
||||||
|
type: "page",
|
||||||
|
title: "CopilotKit Intelligence",
|
||||||
|
icon: "custom/copilotkit-kite",
|
||||||
|
});
|
||||||
|
expect(
|
||||||
|
sectionNodes(navTree, "Intelligence").some(
|
||||||
|
(node) =>
|
||||||
|
node.type === "page" && node.title === "CopilotKit Intelligence",
|
||||||
|
),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
test("resolves canonical contribution aliases through shared Angular content", () => {
|
test("resolves canonical contribution aliases through shared Angular content", () => {
|
||||||
expect(
|
expect(
|
||||||
resolveAngularDoc("claude-sdk-python", "contributing/code-contributions"),
|
resolveAngularDoc("claude-sdk-python", "contributing/code-contributions"),
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ describe("docs mega menu data", () => {
|
|||||||
expect(intelligence).toEqual({
|
expect(intelligence).toEqual({
|
||||||
href: "/intelligence/overview",
|
href: "/intelligence/overview",
|
||||||
label: "Intelligence",
|
label: "Intelligence",
|
||||||
icon: "sparkles",
|
icon: "kite",
|
||||||
featured: true,
|
featured: true,
|
||||||
});
|
});
|
||||||
expect(shipColumn?.links.map((link) => [link.label, link.href])).toEqual([
|
expect(shipColumn?.links.map((link) => [link.label, link.href])).toEqual([
|
||||||
@@ -57,12 +57,13 @@ describe("isIntelligenceDocsPath", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("isDocsExplorePath", () => {
|
describe("isDocsExplorePath", () => {
|
||||||
it("treats guide pages as Explore docs, not Reference or Cookbook", () => {
|
it("treats Intelligence and guide pages as Docs", () => {
|
||||||
expect(isDocsExplorePath("/")).toBe(true);
|
expect(isDocsExplorePath("/")).toBe(true);
|
||||||
expect(isDocsExplorePath("/quickstart")).toBe(true);
|
expect(isDocsExplorePath("/quickstart")).toBe(true);
|
||||||
expect(isDocsExplorePath("/reference")).toBe(false);
|
expect(isDocsExplorePath("/reference")).toBe(false);
|
||||||
expect(isDocsExplorePath("/reference/hooks/useAgent")).toBe(false);
|
expect(isDocsExplorePath("/reference/hooks/useAgent")).toBe(false);
|
||||||
expect(isDocsExplorePath("/cookbook")).toBe(false);
|
expect(isDocsExplorePath("/cookbook")).toBe(false);
|
||||||
expect(isDocsExplorePath("/intelligence/overview")).toBe(false);
|
expect(isDocsExplorePath("/intelligence/overview")).toBe(true);
|
||||||
|
expect(isDocsExplorePath("/premium/overview")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ vi.mock("../registry", async (importOriginal) => {
|
|||||||
import {
|
import {
|
||||||
buildFrameworkNav,
|
buildFrameworkNav,
|
||||||
buildFrameworkOnlyNav,
|
buildFrameworkOnlyNav,
|
||||||
|
buildRootSurfaceNav,
|
||||||
CONTENT_DIR,
|
CONTENT_DIR,
|
||||||
inlineSnippets,
|
inlineSnippets,
|
||||||
loadDoc,
|
loadDoc,
|
||||||
@@ -114,6 +115,30 @@ function sectionPages(navTree: NavNode[], section: string): string[] {
|
|||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sectionTitles(navTree: NavNode[]): string[] {
|
||||||
|
return navTree
|
||||||
|
.filter(
|
||||||
|
(node): node is Extract<NavNode, { type: "section" }> =>
|
||||||
|
node.type === "section",
|
||||||
|
)
|
||||||
|
.map((node) => node.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sectionNodes(navTree: NavNode[], section: string): NavNode[] {
|
||||||
|
const sectionIndex = navTree.findIndex(
|
||||||
|
(node) => node.type === "section" && node.title === section,
|
||||||
|
);
|
||||||
|
if (sectionIndex === -1) return [];
|
||||||
|
|
||||||
|
const nextSectionIndex = navTree.findIndex(
|
||||||
|
(node, index) => index > sectionIndex && node.type === "section",
|
||||||
|
);
|
||||||
|
return navTree.slice(
|
||||||
|
sectionIndex + 1,
|
||||||
|
nextSectionIndex === -1 ? undefined : nextSectionIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function collectMdxFiles(dir: string): string[] {
|
function collectMdxFiles(dir: string): string[] {
|
||||||
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
||||||
const filePath = path.join(dir, entry.name);
|
const filePath = path.join(dir, entry.name);
|
||||||
@@ -201,7 +226,7 @@ describe("loadDoc", () => {
|
|||||||
it("resolves clean URLs to files stored under route-group folders", () => {
|
it("resolves clean URLs to files stored under route-group folders", () => {
|
||||||
const doc = loadDoc("integrations/aws-strands/telemetry");
|
const doc = loadDoc("integrations/aws-strands/telemetry");
|
||||||
|
|
||||||
expect(doc?.filePath).toContain(
|
expect(doc?.filePath.replaceAll("\\", "/")).toContain(
|
||||||
"integrations/aws-strands/(other)/telemetry/index.mdx",
|
"integrations/aws-strands/(other)/telemetry/index.mdx",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -466,6 +491,82 @@ describe("cookbook nav", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("framework nav", () => {
|
describe("framework nav", () => {
|
||||||
|
it("uses the requested section flow on the root docs surface", () => {
|
||||||
|
const navTree = buildRootSurfaceNav("built-in-agent");
|
||||||
|
const titles = sectionTitles(navTree);
|
||||||
|
const gettingStarted = sectionNodes(navTree, "Getting Started");
|
||||||
|
const quickstartIndex = gettingStarted.findIndex(
|
||||||
|
(node) => node.type === "page" && node.slug === "quickstart",
|
||||||
|
);
|
||||||
|
const deployment = sectionNodes(navTree, "Deployment");
|
||||||
|
const runtime = deployment.find(
|
||||||
|
(node) => node.type === "group" && node.title === "Runtime",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(titles).toEqual([
|
||||||
|
"Getting Started",
|
||||||
|
"Basics",
|
||||||
|
"Generative UI",
|
||||||
|
"App Control",
|
||||||
|
"Intelligence",
|
||||||
|
"Built-in Agent",
|
||||||
|
"Deployment",
|
||||||
|
"Concepts",
|
||||||
|
"Other",
|
||||||
|
]);
|
||||||
|
expect(gettingStarted[quickstartIndex + 1]).toMatchObject({
|
||||||
|
type: "page",
|
||||||
|
title: "CopilotKit Intelligence",
|
||||||
|
icon: "custom/copilotkit-kite",
|
||||||
|
});
|
||||||
|
expect(runtime).toMatchObject({ type: "group", title: "Runtime" });
|
||||||
|
expect(
|
||||||
|
runtime?.type === "group" &&
|
||||||
|
hasPageTitle(runtime.children, "Copilot Runtime"),
|
||||||
|
).toBe(true);
|
||||||
|
expect(titles).not.toContain("Runtime");
|
||||||
|
expect(titles).not.toContain("Deploy");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses the same section flow for generated and authored frameworks", () => {
|
||||||
|
const generatedNav = buildFrameworkNav(
|
||||||
|
"langgraph",
|
||||||
|
"LangGraph (Python)",
|
||||||
|
"langgraph-python",
|
||||||
|
);
|
||||||
|
const authoredNav = buildFrameworkOnlyNav("ag2");
|
||||||
|
|
||||||
|
expect(sectionTitles(generatedNav)).toEqual([
|
||||||
|
"Getting Started",
|
||||||
|
"Basics",
|
||||||
|
"Generative UI",
|
||||||
|
"App Control",
|
||||||
|
"Intelligence",
|
||||||
|
"LangGraph (Python)",
|
||||||
|
"Deployment",
|
||||||
|
"Concepts",
|
||||||
|
"Observe & Operate",
|
||||||
|
"Other",
|
||||||
|
]);
|
||||||
|
expect(sectionTitles(authoredNav)).toEqual([
|
||||||
|
"Getting Started",
|
||||||
|
"Basics",
|
||||||
|
"Generative UI",
|
||||||
|
"App Control",
|
||||||
|
"Intelligence",
|
||||||
|
"AG2",
|
||||||
|
"Deployment",
|
||||||
|
"Troubleshooting",
|
||||||
|
"Other",
|
||||||
|
]);
|
||||||
|
expect(hasSectionPage(authoredNav, "Deployment", "Copilot Runtime")).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
hasSectionPage(authoredNav, "Getting Started", "CopilotKit Intelligence"),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("leaves Slack and Teams platform guides ungated", () => {
|
it("leaves Slack and Teams platform guides ungated", () => {
|
||||||
const slack = loadDoc("frontends/slack");
|
const slack = loadDoc("frontends/slack");
|
||||||
const teams = loadDoc("frontends/teams");
|
const teams = loadDoc("frontends/teams");
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ function folderNamed(
|
|||||||
|
|
||||||
describe("navTreeToPageTree section folders", () => {
|
describe("navTreeToPageTree section folders", () => {
|
||||||
const navTree: NavNode[] = [
|
const navTree: NavNode[] = [
|
||||||
|
{ type: "section", title: "Getting Started", icon: "lucide/Rocket" },
|
||||||
|
{ type: "page", title: "Introduction", slug: "" },
|
||||||
{ type: "section", title: "Concepts", icon: "lucide/BookOpen" },
|
{ type: "section", title: "Concepts", icon: "lucide/BookOpen" },
|
||||||
{ type: "page", title: "Architecture", slug: "concepts/architecture" },
|
{ type: "page", title: "Architecture", slug: "concepts/architecture" },
|
||||||
{ type: "page", title: "Which Hook", slug: "concepts/which-hook" },
|
{ type: "page", title: "Which Hook", slug: "concepts/which-hook" },
|
||||||
@@ -54,10 +56,12 @@ describe("navTreeToPageTree section folders", () => {
|
|||||||
|
|
||||||
const pageTree = navTreeToPageTree(navTree, "");
|
const pageTree = navTreeToPageTree(navTree, "");
|
||||||
|
|
||||||
it("turns labeled sections into collapsed folders", () => {
|
it("opens Getting Started and collapses the other labeled sections", () => {
|
||||||
|
const gettingStarted = folderNamed(pageTree.children, "Getting Started");
|
||||||
const concepts = folderNamed(pageTree.children, "Concepts");
|
const concepts = folderNamed(pageTree.children, "Concepts");
|
||||||
const basics = folderNamed(pageTree.children, "Basics");
|
const basics = folderNamed(pageTree.children, "Basics");
|
||||||
|
|
||||||
|
expect(gettingStarted?.defaultOpen).toBe(true);
|
||||||
expect(concepts).toBeDefined();
|
expect(concepts).toBeDefined();
|
||||||
expect(basics).toBeDefined();
|
expect(basics).toBeDefined();
|
||||||
expect(concepts?.defaultOpen).toBe(false);
|
expect(concepts?.defaultOpen).toBe(false);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
buildFrameworkOnlyNav,
|
buildFrameworkOnlyNav,
|
||||||
buildRootSurfaceNav,
|
buildRootSurfaceNav,
|
||||||
loadDoc,
|
loadDoc,
|
||||||
|
normalizeSidebarNav,
|
||||||
} from "./docs-render";
|
} from "./docs-render";
|
||||||
import type { NavNode } from "./docs-render";
|
import type { NavNode } from "./docs-render";
|
||||||
import {
|
import {
|
||||||
@@ -102,11 +103,12 @@ function filterResolvableAngularNodes(
|
|||||||
if (node.type === "page") {
|
if (node.type === "page") {
|
||||||
if (node.slug === "" || node.slug === "quickstart") return [];
|
if (node.slug === "" || node.slug === "quickstart") return [];
|
||||||
const canonicalSlug = getFrontendCanonicalSlug("angular", node.slug);
|
const canonicalSlug = getFrontendCanonicalSlug("angular", node.slug);
|
||||||
if (publishedSlugs.has(canonicalSlug)) {
|
const isIntelligenceOverview = canonicalSlug === "intelligence/overview";
|
||||||
|
if (publishedSlugs.has(canonicalSlug) && !isIntelligenceOverview) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
if (!resolveAngularDoc(backendFramework, canonicalSlug)) return [];
|
if (!resolveAngularDoc(backendFramework, canonicalSlug)) return [];
|
||||||
publishedSlugs.add(canonicalSlug);
|
if (!isIntelligenceOverview) publishedSlugs.add(canonicalSlug);
|
||||||
return [{ ...node, slug: canonicalSlug }];
|
return [{ ...node, slug: canonicalSlug }];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,5 +180,5 @@ export function getAngularDocsNavTree(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return [...prefixPages, ...backendNodes];
|
return normalizeSidebarNav([...prefixPages, ...backendNodes]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export type MegaMenuIconName =
|
|||||||
| "box"
|
| "box"
|
||||||
| "radio"
|
| "radio"
|
||||||
| "brain"
|
| "brain"
|
||||||
| "chart";
|
| "chart"
|
||||||
|
| "kite";
|
||||||
|
|
||||||
export interface MegaMenuLink {
|
export interface MegaMenuLink {
|
||||||
href: string;
|
href: string;
|
||||||
@@ -69,7 +70,7 @@ export const DOCS_MEGA_MENU_COLUMNS: readonly MegaMenuColumn[] = [
|
|||||||
{
|
{
|
||||||
href: INTELLIGENCE_DOCS_HREF,
|
href: INTELLIGENCE_DOCS_HREF,
|
||||||
label: "Intelligence",
|
label: "Intelligence",
|
||||||
icon: "sparkles",
|
icon: "kite",
|
||||||
featured: true,
|
featured: true,
|
||||||
},
|
},
|
||||||
{ href: "/threads", label: "Threads", icon: "layers" },
|
{ href: "/threads", label: "Threads", icon: "layers" },
|
||||||
@@ -109,9 +110,5 @@ export function isIntelligenceDocsPath(pathname: string) {
|
|||||||
|
|
||||||
export function isDocsExplorePath(pathname: string) {
|
export function isDocsExplorePath(pathname: string) {
|
||||||
const firstSegment = pathname === "/" ? "/" : `/${pathname.split("/")[1]}`;
|
const firstSegment = pathname === "/" ? "/" : `/${pathname.split("/")[1]}`;
|
||||||
return (
|
return firstSegment !== "/reference" && firstSegment !== "/cookbook";
|
||||||
firstSegment !== "/reference" &&
|
|
||||||
firstSegment !== "/cookbook" &&
|
|
||||||
firstSegment !== "/intelligence"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,9 +83,11 @@ const SECTION_ICONS: Record<string, string> = {
|
|||||||
"add agent powers": "lucide/Wand2",
|
"add agent powers": "lucide/Wand2",
|
||||||
runtime: "lucide/Cpu",
|
runtime: "lucide/Cpu",
|
||||||
"observe & operate": "lucide/SearchCheck",
|
"observe & operate": "lucide/SearchCheck",
|
||||||
|
intelligence: "custom/copilotkit-kite",
|
||||||
"intelligence platform": "custom/copilotkit-kite",
|
"intelligence platform": "custom/copilotkit-kite",
|
||||||
channels: "lucide/MessagesSquare",
|
channels: "lucide/MessagesSquare",
|
||||||
deploy: "lucide/Cloud",
|
deploy: "lucide/Cloud",
|
||||||
|
deployment: "lucide/Cloud",
|
||||||
other: "lucide/MoreHorizontal",
|
other: "lucide/MoreHorizontal",
|
||||||
// Built-in Agent (authored) sections — match the section names in
|
// Built-in Agent (authored) sections — match the section names in
|
||||||
// `content/docs/integrations/built-in-agent/meta.json`. Adjust here
|
// `content/docs/integrations/built-in-agent/meta.json`. Adjust here
|
||||||
@@ -654,9 +656,11 @@ export function buildFrameworkOnlyNav(
|
|||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
return dropEmptySections(
|
return normalizeSidebarNav(
|
||||||
appendSharedThreadArchitecturePage(
|
dropEmptySections(
|
||||||
appendSharedRootSections(nodes.map(rewrite), sharedSections),
|
appendSharedThreadArchitecturePage(
|
||||||
|
appendSharedRootSections(nodes.map(rewrite), sharedSections),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -757,6 +761,172 @@ function findPageBySlug(navTree: NavNode[], slug: string): NavNode | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SidebarSection = {
|
||||||
|
section: Extract<NavNode, { type: "section" }>;
|
||||||
|
children: NavNode[];
|
||||||
|
sourceIndex: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SIDEBAR_SECTION_TITLES: Record<string, string> = {
|
||||||
|
"get started": "Getting Started",
|
||||||
|
"getting started": "Getting Started",
|
||||||
|
"build chat uis": "Basics",
|
||||||
|
basics: "Basics",
|
||||||
|
"build generative ui": "Generative UI",
|
||||||
|
"generative ui": "Generative UI",
|
||||||
|
"add agent powers": "App Control",
|
||||||
|
"app control": "App Control",
|
||||||
|
intelligence: "Intelligence",
|
||||||
|
"intelligence platform": "Intelligence",
|
||||||
|
backend: "Runtime",
|
||||||
|
runtime: "Runtime",
|
||||||
|
deploy: "Deployment",
|
||||||
|
deployment: "Deployment",
|
||||||
|
};
|
||||||
|
|
||||||
|
function sidebarSectionRank(title: string): number {
|
||||||
|
switch (title) {
|
||||||
|
case "Getting Started":
|
||||||
|
return 0;
|
||||||
|
case "Basics":
|
||||||
|
return 1;
|
||||||
|
case "Generative UI":
|
||||||
|
return 2;
|
||||||
|
case "App Control":
|
||||||
|
return 3;
|
||||||
|
case "Intelligence":
|
||||||
|
return 4;
|
||||||
|
case "Deployment":
|
||||||
|
return 6;
|
||||||
|
case "Concepts":
|
||||||
|
case "Observe & Operate":
|
||||||
|
case "Platforms":
|
||||||
|
case "Tutorials":
|
||||||
|
case "Troubleshooting":
|
||||||
|
case "Other":
|
||||||
|
return 7;
|
||||||
|
default:
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeSidebarNav(navTree: NavNode[]) {
|
||||||
|
const prefix: NavNode[] = [];
|
||||||
|
const sections: SidebarSection[] = [];
|
||||||
|
const sectionsByTitle = new Map<string, SidebarSection>();
|
||||||
|
let current: SidebarSection | undefined;
|
||||||
|
|
||||||
|
for (const node of navTree) {
|
||||||
|
if (node.type === "section") {
|
||||||
|
const title =
|
||||||
|
SIDEBAR_SECTION_TITLES[node.title.toLowerCase()] ?? node.title;
|
||||||
|
const existing = sectionsByTitle.get(title);
|
||||||
|
if (existing) {
|
||||||
|
current = existing;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
current = {
|
||||||
|
section: {
|
||||||
|
...node,
|
||||||
|
title,
|
||||||
|
icon: node.icon ?? sectionIconFor(title),
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
sourceIndex: sections.length,
|
||||||
|
};
|
||||||
|
sections.push(current);
|
||||||
|
sectionsByTitle.set(title, current);
|
||||||
|
} else if (current) {
|
||||||
|
current.children.push(node);
|
||||||
|
} else {
|
||||||
|
prefix.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gettingStarted = sections.find(
|
||||||
|
({ section }) => section.title === "Getting Started",
|
||||||
|
);
|
||||||
|
if (prefix.length > 0) {
|
||||||
|
if (!gettingStarted) {
|
||||||
|
gettingStarted = {
|
||||||
|
section: {
|
||||||
|
type: "section",
|
||||||
|
title: "Getting Started",
|
||||||
|
icon: sectionIconFor("Getting Started"),
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
sourceIndex: -1,
|
||||||
|
};
|
||||||
|
sections.push(gettingStarted);
|
||||||
|
}
|
||||||
|
gettingStarted.children.unshift(...prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
const runtimeSections = sections.filter(
|
||||||
|
({ section }) => section.title === "Runtime",
|
||||||
|
);
|
||||||
|
const orderedSections = sections.filter(
|
||||||
|
({ section }) => section.title !== "Runtime",
|
||||||
|
);
|
||||||
|
const runtimeChildren = runtimeSections.flatMap(({ children }) => children);
|
||||||
|
if (runtimeChildren.length > 0) {
|
||||||
|
let deployment = orderedSections.find(
|
||||||
|
({ section }) => section.title === "Deployment",
|
||||||
|
);
|
||||||
|
if (!deployment) {
|
||||||
|
deployment = {
|
||||||
|
section: {
|
||||||
|
type: "section",
|
||||||
|
title: "Deployment",
|
||||||
|
icon: sectionIconFor("Deployment"),
|
||||||
|
},
|
||||||
|
children: [],
|
||||||
|
sourceIndex: sections.length,
|
||||||
|
};
|
||||||
|
orderedSections.push(deployment);
|
||||||
|
}
|
||||||
|
deployment.children.push({
|
||||||
|
type: "group",
|
||||||
|
title: "Runtime",
|
||||||
|
slug: "sidebar#runtime",
|
||||||
|
children: runtimeChildren,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const intelligence = orderedSections.find(
|
||||||
|
({ section }) => section.title === "Intelligence",
|
||||||
|
);
|
||||||
|
const intelligenceOverview = intelligence
|
||||||
|
? findPageBySlug(intelligence.children, "intelligence/overview")
|
||||||
|
: null;
|
||||||
|
if (gettingStarted && intelligenceOverview?.type === "page") {
|
||||||
|
const existingIndex = gettingStarted.children.findIndex(
|
||||||
|
(node) => node.type === "page" && node.slug === intelligenceOverview.slug,
|
||||||
|
);
|
||||||
|
if (existingIndex !== -1) gettingStarted.children.splice(existingIndex, 1);
|
||||||
|
|
||||||
|
const quickstartIndex = gettingStarted.children.findIndex(
|
||||||
|
(node) =>
|
||||||
|
node.type === "page" &&
|
||||||
|
(node.slug === "quickstart" ||
|
||||||
|
node.title.toLowerCase().endsWith("quickstart")),
|
||||||
|
);
|
||||||
|
gettingStarted.children.splice(quickstartIndex + 1, 0, {
|
||||||
|
...intelligenceOverview,
|
||||||
|
icon: "custom/copilotkit-kite",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return orderedSections
|
||||||
|
.sort((left, right) => {
|
||||||
|
const rankDifference =
|
||||||
|
sidebarSectionRank(left.section.title) -
|
||||||
|
sidebarSectionRank(right.section.title);
|
||||||
|
return rankDifference || left.sourceIndex - right.sourceIndex;
|
||||||
|
})
|
||||||
|
.flatMap(({ section, children }) => [section, ...children]);
|
||||||
|
}
|
||||||
|
|
||||||
function isRichThreadsGroup(
|
function isRichThreadsGroup(
|
||||||
node: NavNode,
|
node: NavNode,
|
||||||
): node is Extract<NavNode, { type: "group" }> {
|
): node is Extract<NavNode, { type: "group" }> {
|
||||||
@@ -977,11 +1147,13 @@ export function buildFrameworkNav(
|
|||||||
frameworkName: string,
|
frameworkName: string,
|
||||||
frameworkSlug: string,
|
frameworkSlug: string,
|
||||||
): NavNode[] {
|
): NavNode[] {
|
||||||
return mergeFrameworkNav(
|
return normalizeSidebarNav(
|
||||||
buildNavTree(CONTENT_DIR),
|
mergeFrameworkNav(
|
||||||
buildFrameworkOverridesNav(docsFolder),
|
buildNavTree(CONTENT_DIR),
|
||||||
frameworkName,
|
buildFrameworkOverridesNav(docsFolder),
|
||||||
frameworkSectionIcon(frameworkSlug),
|
frameworkName,
|
||||||
|
frameworkSectionIcon(frameworkSlug),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -302,7 +302,9 @@ export function wrapSectionSeparatorsAsFolders(
|
|||||||
...(pending.icon
|
...(pending.icon
|
||||||
? { icon: keyedNode(pending.icon, "shell-docs-section-icon") }
|
? { icon: keyedNode(pending.icon, "shell-docs-section-icon") }
|
||||||
: {}),
|
: {}),
|
||||||
defaultOpen: false,
|
defaultOpen:
|
||||||
|
typeof pending.name === "string" &&
|
||||||
|
["Get Started", "Getting Started"].includes(pending.name),
|
||||||
children: pending.children,
|
children: pending.children,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { LanggraphIcon } from "@/components/icons/framework-icons";
|
import { LanggraphIcon } from "@/components/icons/framework-icons";
|
||||||
import { CopilotKitMark } from "@/components/copilotkit-mark";
|
import { CopilotKitIcon } from "@/components/copilotkit-icon";
|
||||||
import { FrontendLogo } from "@/components/frontend-logo";
|
import { FrontendLogo } from "@/components/frontend-logo";
|
||||||
import {
|
import {
|
||||||
// Pages / sections
|
// Pages / sections
|
||||||
@@ -140,11 +140,10 @@ const ICONS: Record<string, React.ReactNode> = {
|
|||||||
"lucide/Wrench": <Wrench />,
|
"lucide/Wrench": <Wrench />,
|
||||||
"lucide/Zap": <Zap />,
|
"lucide/Zap": <Zap />,
|
||||||
// Custom marks — used by section headers for framework / enterprise
|
// Custom marks — used by section headers for framework / enterprise
|
||||||
// scaffolding. The CopilotKit kite is the inline gradient mark from
|
// scaffolding. The LangGraph mark comes from the framework-icons set so
|
||||||
// BrandNav; the LangGraph mark comes from the framework-icons set so
|
|
||||||
// its visual treatment matches the framework picker.
|
// its visual treatment matches the framework picker.
|
||||||
"custom/langgraph": <LanggraphIcon />,
|
"custom/langgraph": <LanggraphIcon />,
|
||||||
"custom/copilotkit-kite": <CopilotKitMark />,
|
"custom/copilotkit-kite": <CopilotKitIcon />,
|
||||||
"custom/react": <FrontendLogo icon="react" size={16} />,
|
"custom/react": <FrontendLogo icon="react" size={16} />,
|
||||||
"custom/daytona": (
|
"custom/daytona": (
|
||||||
<Image
|
<Image
|
||||||
|
|||||||
Reference in New Issue
Block a user