Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion apps/website/components/ai/ask-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
useRef,
useState,
} from "react";
import { track } from "@vercel/analytics";
import { Loader2, X } from "lucide-react";
import { cn } from "@/lib/cn";
import Link from "fumadocs-core/link";
Expand Down Expand Up @@ -118,12 +119,18 @@ function AskAIInput({
onEscape?: () => void;
showEscButton?: boolean;
}) {
const { status, sendMessage } = useChatContext();
const { status, sendMessage, messages } = useChatContext();
const [input, setInput] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
const isLoading = status === "streaming" || status === "submitted";
const onStart = () => {
if (input.trim()) {
track("ai_question_submitted", {
question: input.trim(),
location: "Ask AI Dialog",
not_first_message: messages.length > 0,
});

void sendMessage({ text: input });
setInput("");
}
Expand Down
19 changes: 18 additions & 1 deletion apps/website/components/animated-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,46 @@ import Link from "next/link";
import { cn } from "@/lib/cn";
import { usePathname } from "next/navigation";
import { forwardRef } from "react";
import { track } from "@vercel/analytics";

interface AnimatedLinkProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
href: string;
children: React.ReactNode;
className?: string;
trackIntent?: string;
trackLocation?: string;
}

export const AnimatedLink = forwardRef<HTMLAnchorElement, AnimatedLinkProps>(
({ href, children, className = "", ...props }, ref) => {
(
{ trackIntent, trackLocation, href, children, className = "", ...props },
ref
) => {
const pathname = usePathname();
// caveats
const isActive =
pathname === href ||
(href === "/docs" && pathname.startsWith("/docs")) ||
(href === "/blog" && pathname.startsWith("/blog"));

const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
track("link clicked", {
location: trackLocation || pathname || "unknown",
intent: trackIntent || "navigation",
href: href,
});

props.onClick?.(event);
};

return (
<Link
href={href}
ref={ref}
{...props}
className={cn("relative group", className)}
onClick={handleClick}
>
{children}
<div
Expand Down
14 changes: 12 additions & 2 deletions apps/website/components/home/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,20 @@ export const HomeHero = () => {
/> */}
<Shader />
<div className="flex items-center justify-center gap-2">
<Button variant="primary" asChild>
<Button
variant="primary"
asChild
trackIntent="get started"
trackLocation="home hero"
>
<Link href="/docs">Get started</Link>
</Button>
<Button variant="secondary" asChild>
<Button
variant="secondary"
asChild
trackIntent="deploy to vercel"
trackLocation="home hero"
>
<Link
href="https://vercel.com/new/clone?demo-description=The%20TypeScript%20MCP%20framework&demo-image=%2F%2Fimages.ctfassets.net%2Fe5382hct74si%2FasHTCFA47swQRiCYbWJDl%2F85fc42d9ef2dae4312744a964251f223%2Fimage__7_.png&demo-title=xmcp%20boilerplate&demo-url=https%3A%2F%2Fxmcp-template.vercel.app%2F&from=templates&project-name=xmcp%20boilerplate&project-names=Comma%20separated%20list%20of%20project%20names%2Cto%20match%20the%20root-directories&repository-name=xmcp-boilerplate&repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fexamples%2Ftree%2Fmain%2Fframework-boilerplates%2Fxmcp&root-directories=List%20of%20directory%20paths%20for%20the%20directories%20to%20clone%20into%20projects&skippable-integrations=1"
target="_blank"
Expand Down
15 changes: 15 additions & 0 deletions apps/website/components/page-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,19 @@ import { cn } from "../lib/cn";
import { useCopyButton } from "fumadocs-ui/utils/use-copy-button";
import { Icons } from "./icons";
import Link from "next/link";
import { track } from "@vercel/analytics";

const cache = new Map<string, string>();

export function PageActions({ markdownUrl }: { markdownUrl: string }) {
const [isLoading, setLoading] = useState(false);
const [checked, onClick] = useCopyButton(async () => {
track("docs action clicked", {
action: "copy_markdown",
markdownUrl: markdownUrl,
location: "docs_page_actions",
});

const cached = cache.get(markdownUrl);
if (cached) return navigator.clipboard.writeText(cached);

Expand Down Expand Up @@ -111,6 +118,14 @@ export function PageActions({ markdownUrl }: { markdownUrl: string }) {
className={cn(
"text-sm py-1 px-0 inline-flex items-center gap-2 text-brand-neutral-100 hover:text-brand-white transition-colors duration-200 bg-transparent hover:bg-transparent [&_svg]:size-3 justify-start border-0"
)}
onClick={() => {
track("docs action clicked", {
action: item.title.toLowerCase().replace(/\s+/g, "_"),
markdownUrl: markdownUrl,
location: "docs_page_actions",
destination: item.href.toString(),
});
}}
>
{item.icon}
{item.title}
Expand Down
10 changes: 8 additions & 2 deletions apps/website/components/showcase/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useState, useEffect } from "react";
import { createShowcaseSubmission } from "./actions";
import Image from "next/image";
import { track } from "@vercel/analytics";
import { cn } from "../../utils/cn";

interface FormData {
Expand Down Expand Up @@ -117,6 +118,11 @@ export function ShowcaseForm() {

if (result.success) {
setShowSuccessMessage(true);

track("showcase_submission", {
name: formData.name,
repositoryUrl: formData.repositoryUrl,
});
} else {
if (result.errors) {
setErrors(result.errors);
Expand Down Expand Up @@ -302,8 +308,8 @@ export function ShowcaseForm() {
errors.logo
? "border border-red-500"
: logoPreview
? "border border-[#333333]"
: "border border-dashed border-[#333333]",
? "border border-[#333333]"
: "border border-dashed border-[#333333]",
showSuccessMessage &&
"opacity-50 cursor-not-allowed hover:bg-transparent"
)}
Expand Down
31 changes: 30 additions & 1 deletion apps/website/components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"use client";

import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/cn";
import { track } from "@vercel/analytics";

const variants = {
primary:
Expand Down Expand Up @@ -31,13 +34,39 @@ interface ButtonProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "color">,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
trackIntent?: string;
trackLocation?: string;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, color, size, asChild = false, ...props }, ref) => {
(
{
trackIntent,
trackLocation,
className,
variant,
color,
size,
asChild = false,
onClick,
...props
},
ref
) => {
const Comp = asChild ? Slot : "button";

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
track("button clicked", {
location: trackLocation || window?.location?.pathname || "unknown",
intent: trackIntent || "unknown",
});

onClick?.(event);
};

return (
<Comp
onClick={handleClick}
className={cn(buttonVariants({ variant, color, size, className }))}
ref={ref}
{...props}
Expand Down