mirror of
https://github.com/vercel/chat.git
synced 2026-09-14 18:32:29 +08:00
b0ab804f18
- Add scripts/sync-resources.ts (run via pnpm sync-resources) that reads apps/docs/resources-edge-config.json, fetches each guide's .md version over https with a timeout and size cap, writes templates.json, and regenerates the Available resources block in skills/chat/SKILL.md - Migrate the Slack Next.js, Discord Nuxt, and Hono code-review guides from on-site MDX to Vercel KB and register them in the resources edge-config JSON alongside the existing external guides - Remove /docs/guides MDX content, sidebar entries, top-level Guides nav entry, getting-started cards, and the dead /guides/ branch in the sitemap route now that all guides live externally and are surfaced on /resources - Replace the homepage Guides/Templates section and the standalone Adapters pill section with a single two-column Resources + Adapters section (icons, headings, descriptions, outline buttons, divider), and drop the URL footer from ResourceCard on the Resources page - Update skills/chat/SKILL.md to point at resources/guides and resources/templates.json and list the available guides and templates between marker comments that sync-resources rewrites - Add tsx to knip's ignoreBinaries so npx tsx in the new script does not fail lint Co-authored-by: Ben Sabic <bensabic@users.noreply.github.com> Co-authored-by: dancer <josh@afterima.ge>
82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import DynamicLink from "fumadocs-core/dynamic-link";
|
|
import { usePathname } from "next/navigation";
|
|
import { IconArrowUpRightSmall } from "@/components/geistcn-fallbacks/geistcn-assets/icons/icon-arrow-up-right-small";
|
|
import {
|
|
NavigationMenu,
|
|
NavigationMenuItem,
|
|
NavigationMenuLink,
|
|
NavigationMenuList,
|
|
} from "@/components/ui/navigation-menu";
|
|
import { useIsMobile } from "@/hooks/use-mobile";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface DesktopMenuProps {
|
|
className?: string;
|
|
items: { label: string; href: string }[];
|
|
}
|
|
|
|
// Nav hrefs can be deep links (e.g. `/docs/api/chat`), but the tab
|
|
// represents the whole section. Use the first three path segments as
|
|
// the matching prefix.
|
|
const getSectionPrefix = (href: string) =>
|
|
href.split("/").slice(0, 3).join("/");
|
|
|
|
export const DesktopMenu = ({ items, className }: DesktopMenuProps) => {
|
|
const isMobile = useIsMobile();
|
|
const pathname = usePathname();
|
|
|
|
// Pick the single longest-matching section (so `Docs` doesn't stay
|
|
// active when a more specific section like `API` also matches).
|
|
const activeHref = items.reduce<string | null>((winner, item) => {
|
|
if (item.href.startsWith("http")) return winner;
|
|
const prefix = getSectionPrefix(item.href);
|
|
const matches =
|
|
pathname.endsWith(prefix) || pathname.includes(`${prefix}/`);
|
|
if (!matches) return winner;
|
|
if (!winner) return item.href;
|
|
return getSectionPrefix(item.href).length >
|
|
getSectionPrefix(winner).length
|
|
? item.href
|
|
: winner;
|
|
}, null);
|
|
|
|
return (
|
|
<NavigationMenu viewport={isMobile}>
|
|
<NavigationMenuList className={cn("h-14 gap-4", className)}>
|
|
{items.map((item) => {
|
|
const isExternal = item.href.startsWith("http");
|
|
const isActive = !isExternal && item.href === activeHref;
|
|
|
|
return (
|
|
<NavigationMenuItem key={item.href}>
|
|
<NavigationMenuLink
|
|
active={isActive}
|
|
asChild
|
|
className="flex items-center text-gray-900 text-sm transition-colors duration-100 hover:text-gray-1000 data-[active]:text-gray-1000"
|
|
>
|
|
{isExternal ? (
|
|
<a
|
|
className="flex flex-row items-center gap-1"
|
|
href={item.href}
|
|
rel="noopener"
|
|
target="_blank"
|
|
>
|
|
{item.label}
|
|
<IconArrowUpRightSmall aria-hidden="true" size={12} />
|
|
</a>
|
|
) : (
|
|
<DynamicLink href={`/[lang]${item.href}`}>
|
|
{item.label}
|
|
</DynamicLink>
|
|
)}
|
|
</NavigationMenuLink>
|
|
</NavigationMenuItem>
|
|
);
|
|
})}
|
|
</NavigationMenuList>
|
|
</NavigationMenu>
|
|
);
|
|
};
|