Merge pull request #537 from sarinali/feat/llm-compatible-docs

Robots + Advanced Copy Options
This commit is contained in:
James Murdza
2025-10-31 14:56:55 -07:00
committed by GitHub
5 changed files with 167 additions and 2 deletions

View File

@@ -19,6 +19,7 @@
"posthog-js": "^1.276.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-icons": "^5.5.0",
"remark": "^15.0.1",
"remark-gfm": "^4.0.1",
"remark-mdx": "^3.1.0",

12
docs/pnpm-lock.yaml generated
View File

@@ -38,6 +38,9 @@ importers:
react-dom:
specifier: ^19.1.0
version: 19.1.0(react@19.1.0)
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@19.1.0)
remark:
specifier: ^15.0.1
version: 15.0.1
@@ -2054,6 +2057,11 @@ packages:
peerDependencies:
react: ^19.1.0
react-icons@5.5.0:
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
peerDependencies:
react: '*'
react-medium-image-zoom@5.2.14:
resolution: {integrity: sha512-nfTVYcAUnBzXQpPDcZL+cG/e6UceYUIG+zDcnemL7jtAqbJjVVkA85RgneGtJeni12dTyiRPZVM6Szkmwd/o8w==}
peerDependencies:
@@ -4622,6 +4630,10 @@ snapshots:
react: 19.1.0
scheduler: 0.26.0
react-icons@5.5.0(react@19.1.0):
dependencies:
react: 19.1.0
react-medium-image-zoom@5.2.14(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0

View File

@@ -10,6 +10,7 @@ import type { Metadata } from 'next';
import Link from 'next/link';
import { notFound, redirect } from 'next/navigation';
import { PageFeedback } from '@/components/page-feedback';
import { DocActionsMenu } from '@/components/doc-actions-menu';
export default async function Page(props: { params: Promise<{ slug?: string[] }> }) {
const params = await props.params;
@@ -177,14 +178,26 @@ export default async function Page(props: { params: Promise<{ slug?: string[] }>
);
};
const tocFooter = () => {
return (
<div className="mt-4">
<DocActionsMenu pageUrl={page.url} pageTitle={page.data.title} filePath={page.file.path} />
</div>
);
};
return (
<DocsPage toc={page.data.toc} tableOfContent={{ header: tocHeader() }} full={page.data.full}>
<DocsPage
toc={page.data.toc}
tableOfContent={{ header: tocHeader(), footer: tocFooter() }}
full={page.data.full}
>
<div className="flex flex-row w-full items-start">
<div className="flex-1">
<div className="flex flex-row w-full">
<DocsTitle>{page.data.title}</DocsTitle>
<div className="ml-auto">
<div className="ml-auto flex items-center gap-2">
{apiSection && versionItems.length > 1 && (
<Popover>
<PopoverTrigger

13
docs/src/app/robots.ts Normal file
View File

@@ -0,0 +1,13 @@
import { MetadataRoute } from 'next';
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: '*',
allow: ['/', '/llms.txt'],
disallow: [],
},
sitemap: 'https://cua.ai/docs/sitemap.xml',
host: 'https://cua.ai',
};
}

View File

@@ -0,0 +1,126 @@
'use client';
import { useState } from 'react';
import { SiOpenai, SiAnthropic, SiMarkdown, SiGithub } from 'react-icons/si';
import posthog from 'posthog-js';
interface DocActionsMenuProps {
pageUrl: string;
pageTitle: string;
filePath: string;
}
export function DocActionsMenu({ pageUrl, pageTitle, filePath }: DocActionsMenuProps) {
const [copied, setCopied] = useState(false);
const handleCopyMarkdown = async () => {
try {
const githubRawUrl = `https://raw.githubusercontent.com/trycua/cua/refs/heads/main/docs/content/docs/${filePath}`;
const response = await fetch(githubRawUrl);
if (!response.ok) {
throw new Error('Failed to fetch markdown');
}
const markdown = await response.text();
await navigator.clipboard.writeText(markdown);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
posthog.capture('docs_copy_markdown_clicked', {
page: pageUrl,
page_title: pageTitle,
success: true,
});
} catch (error) {
console.error('Error copying markdown:', error);
try {
const urlWithUtm = `https://cua.ai${pageUrl}?utm_source=cua.ai/docs`;
await navigator.clipboard.writeText(urlWithUtm);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (fallbackError) {
console.error('Error copying URL:', fallbackError);
}
posthog.capture('docs_copy_markdown_clicked', {
page: pageUrl,
page_title: pageTitle,
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
};
const handleEditGithub = () => {
posthog.capture('docs_edit_github_clicked', {
page: pageUrl,
page_title: pageTitle,
});
const githubEditUrl = `https://github.com/trycua/cua/edit/main/docs/content/docs/${filePath}`;
window.open(githubEditUrl, '_blank', 'noopener,noreferrer');
};
const handleOpenChatGPT = () => {
posthog.capture('docs_open_chatgpt_clicked', {
page: pageUrl,
page_title: pageTitle,
});
const docUrl = `https://cua.ai${pageUrl}?utm_source=cua.ai/docs`;
const prompt = `I need help understanding this cua.ai documentation page: "${pageTitle}". Please read and help me with: ${docUrl}`;
const chatgptUrl = `https://chatgpt.com/?q=${encodeURIComponent(prompt)}`;
window.open(chatgptUrl, '_blank', 'noopener,noreferrer');
};
const handleOpenClaude = () => {
posthog.capture('docs_open_claude_clicked', {
page: pageUrl,
page_title: pageTitle,
});
const docUrl = `https://cua.ai${pageUrl}?utm_source=cua.ai/docs`;
const prompt = `I need help understanding this cua.ai documentation page: "${pageTitle}". Please read and help me with: ${docUrl}`;
const claudeUrl = `https://claude.ai/new?q=${encodeURIComponent(prompt)}`;
window.open(claudeUrl, '_blank', 'noopener,noreferrer');
};
return (
<div className="flex flex-col gap-2">
<button
onClick={handleCopyMarkdown}
className="inline-flex gap-3 w-full items-center rounded-md p-1 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground text-left transition-colors px-2 hover:cursor-pointer"
>
<SiMarkdown className="w-2 h-4 flex-shrink-0" />
<span>{copied ? 'Copied!' : 'Copy as markdown'}</span>
</button>
<button
onClick={handleEditGithub}
className="inline-flex gap-3 w-full items-center rounded-md p-1 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground text-left transition-colors px-2 hover:cursor-pointer"
>
<SiGithub className="w-4 h-4 flex-shrink-0" />
<span>Edit on GitHub</span>
</button>
<button
onClick={handleOpenChatGPT}
className="inline-flex gap-3 w-full items-center rounded-md p-1 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground text-left transition-colors px-2 hover:cursor-pointer"
>
<SiOpenai className="w-4 h-4 flex-shrink-0" />
<span>Open in ChatGPT</span>
</button>
<button
onClick={handleOpenClaude}
className="inline-flex gap-3 w-full items-center rounded-md p-1 text-sm hover:bg-fd-accent hover:text-fd-accent-foreground text-left transition-colors px-2 hover:cursor-pointer"
>
<SiAnthropic className="w-4 h-4 flex-shrink-0" />
<span>Open in Claude</span>
</button>
</div>
);
}