Skip to content

Commit e7905a9

Browse files
committed
docs: add copy md button
1 parent 6e8c04d commit e7905a9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+857
-2
lines changed

website/next.config.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ const nextConfig = {
1717
},
1818
]
1919
},
20+
async rewrites() {
21+
return [
22+
{
23+
source: '/docs/:path*.mdx',
24+
destination: '/llms.txt/:path*.mdx',
25+
},
26+
]
27+
},
2028
}
2129

2230
const isDev = process.argv.indexOf('dev') !== -1

website/src/app/docs/[...slug]/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Metadata } from 'next'
22
import { notFound } from 'next/navigation'
33
import { css } from 'styled-system/css'
44
import { Box, Container, Stack } from 'styled-system/jsx'
5+
import { CopyPageWidget } from '~/components/copy-page-widget'
56
import { DocsFooter } from '~/components/navigation/docs/docs-footer'
67
import { TableOfContent } from '~/components/table-of-content'
78
import { Heading } from '~/components/ui/heading'
@@ -31,6 +32,7 @@ export default async function Page(props: Props) {
3132
<Stack gap="16" px={{ base: '0', xl: '8' }} width="full">
3233
<article
3334
className={css({
35+
position: 'relative',
3436
lineHeight: '1.75',
3537
color: 'var(--colors-prose-body)',
3638
maxW: '45rem',
@@ -44,6 +46,9 @@ export default async function Page(props: Props) {
4446
<Text className="lead" color="fg.muted" my="6" size="xl">
4547
{currentPage.description}
4648
</Text>
49+
<Box position={{ md: 'absolute' }} top="2" right="2">
50+
<CopyPageWidget slug={currentPage.slug} content={currentPage.llm} />
51+
</Box>
4752
<MDXContent code={currentPage.code} />
4853
</article>
4954

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { notFound } from 'next/navigation'
2+
import { getSidebarGroups } from '~/lib/sidebar'
3+
4+
interface RouteContext {
5+
params: Promise<{ slug: string[] }>
6+
}
7+
8+
const pages = getSidebarGroups().flat()
9+
10+
export async function GET(_request: Request, context: RouteContext) {
11+
const params = await context.params
12+
let slugParts = params.slug
13+
14+
const lastPart = slugParts[slugParts.length - 1]
15+
if (lastPart.endsWith('.mdx')) {
16+
slugParts = [...slugParts.slice(0, -1), lastPart.slice(0, -4)]
17+
}
18+
19+
const fullSlug = slugParts.join('/')
20+
const page = pages.find((p) => p.slug === fullSlug)
21+
22+
if (!page || !page.llm) {
23+
notFound()
24+
}
25+
26+
const content = generateLlmContent(page)
27+
28+
return new Response(content, {
29+
headers: {
30+
'Content-Type': 'text/plain; charset=utf-8',
31+
'Cache-Control': 'public, max-age=3600',
32+
},
33+
})
34+
}
35+
36+
function generateLlmContent(page: (typeof pages)[0]) {
37+
const sourceUrl = `https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/${page.slug}.mdx`
38+
39+
const header = `# ${page.title}
40+
41+
URL: https://ark-ui.com/docs/${page.slug}
42+
Source: ${sourceUrl}
43+
44+
${page.description || ''}
45+
46+
---
47+
48+
`
49+
50+
return `${header}${page.llm}`
51+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Box, Stack } from 'styled-system/jsx'
2+
import { Code } from '~/components/ui/code'
3+
import { Table } from '~/components/ui/table'
4+
import { Text } from '~/components/ui/text'
5+
6+
interface Props {
7+
api: Record<
8+
string,
9+
{
10+
type: string
11+
description: string
12+
}
13+
>
14+
}
15+
16+
export const ApiTable = (props: Props) => {
17+
const { api } = props
18+
19+
return (
20+
<Box borderWidth="1px" borderRadius="lg" overflowX="auto" className="not-prose" my="8">
21+
<Table.Root variant="outline" size="sm" border={0}>
22+
<Table.Head>
23+
<Table.Row>
24+
<Table.Header px="4" bg="gray.2" h="10">
25+
Property
26+
</Table.Header>
27+
<Table.Header px="4" bg="gray.2" h="10">
28+
Type
29+
</Table.Header>
30+
</Table.Row>
31+
</Table.Head>
32+
<Table.Body>
33+
{Object.entries(api).map(([name, property]) => (
34+
<Table.Row key={name}>
35+
<Table.Cell width="36" px="4" py="2" verticalAlign="top">
36+
<Code size="sm" color="colorPalette.default">
37+
{name}
38+
</Code>
39+
</Table.Cell>
40+
<Table.Cell px="4" py="2" verticalAlign="top">
41+
<Stack gap="1" align="start">
42+
<Code size="sm">{property.type}</Code>
43+
<Text>{property.description}</Text>
44+
</Stack>
45+
</Table.Cell>
46+
</Table.Row>
47+
))}
48+
</Table.Body>
49+
</Table.Root>
50+
</Box>
51+
)
52+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { MinusIcon } from 'lucide-react'
2+
import { Box, Stack } from 'styled-system/jsx'
3+
import { Code } from '~/components/ui/code'
4+
import { Icon } from '~/components/ui/icon'
5+
import { Table } from '~/components/ui/table'
6+
import { Text } from '~/components/ui/text'
7+
8+
interface Props {
9+
context: Record<
10+
string,
11+
{
12+
type: string
13+
description: string
14+
defaultValue?: string | undefined
15+
}
16+
>
17+
}
18+
19+
export const ContextTable = (props: Props) => {
20+
const { context } = props
21+
22+
return (
23+
<Box borderWidth="1px" borderRadius="lg" overflowX="auto" className="not-prose" my="8">
24+
<Table.Root variant="outline" size="sm" border={0}>
25+
<Table.Head>
26+
<Table.Row>
27+
<Table.Header px="4" bg="gray.2" h="10">
28+
Property
29+
</Table.Header>
30+
<Table.Header px="4" bg="gray.2" h="10">
31+
Default
32+
</Table.Header>
33+
<Table.Header px="4" bg="gray.2" h="10">
34+
Type
35+
</Table.Header>
36+
</Table.Row>
37+
</Table.Head>
38+
<Table.Body>
39+
{Object.entries(context).map(([name, property]) => (
40+
<Table.Row key={name}>
41+
<Table.Cell width="36" px="4" py="2" verticalAlign="top">
42+
<Code size="sm" color="colorPalette.default">
43+
{name}
44+
</Code>
45+
</Table.Cell>
46+
<Table.Cell width="28" px="4" py="2" verticalAlign="top">
47+
{property.defaultValue ? (
48+
<Code size="sm">{property.defaultValue.replaceAll('"', "'")}</Code>
49+
) : (
50+
<Icon size="xs" color="fg.muted">
51+
<MinusIcon />
52+
</Icon>
53+
)}
54+
</Table.Cell>
55+
<Table.Cell px="4" py="2" verticalAlign="top">
56+
<Stack gap="1" align="start">
57+
<Code size="sm">{property.type}</Code>
58+
<Text>{property.description}</Text>
59+
</Stack>
60+
</Table.Cell>
61+
</Table.Row>
62+
))}
63+
</Table.Body>
64+
</Table.Root>
65+
</Box>
66+
)
67+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { getApiDoc } from '@zag-js/docs'
2+
import { Fragment } from 'react'
3+
import { Heading } from '~/components/ui/heading'
4+
import { ApiTable } from './api-table'
5+
6+
interface Props {
7+
id: string
8+
}
9+
10+
export const ContextType = (props: Props) => {
11+
try {
12+
const apiDoc = getApiDoc(props.id as any)
13+
14+
return (
15+
<Fragment>
16+
{apiDoc.api && Object.keys(apiDoc.api).length > 0 && (
17+
<>
18+
<Heading as="h3" size="xl">
19+
API
20+
</Heading>
21+
<ApiTable api={apiDoc.api} />
22+
</>
23+
)}
24+
</Fragment>
25+
)
26+
} catch {
27+
return null
28+
}
29+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
'use client'
2+
3+
import { useClipboard } from '@ark-ui/react/clipboard'
4+
import { Portal } from '@ark-ui/react/portal'
5+
import { SiMarkdown } from '@icons-pack/react-simple-icons'
6+
import { CheckIcon, ChevronDownIcon } from 'lucide-react'
7+
import { Box, HStack } from 'styled-system/jsx'
8+
import { Button } from '~/components/ui/button'
9+
import { IconButton } from '~/components/ui/icon-button'
10+
import { Menu } from '~/components/ui/menu'
11+
import { getPublicUrl } from '~/lib/get-public-url'
12+
13+
interface CopyPageWidgetProps {
14+
slug: string
15+
content: string
16+
}
17+
18+
export const CopyPageWidget = (props: CopyPageWidgetProps) => {
19+
const { slug, content } = props
20+
return (
21+
<HStack gap="0" spaceX="-1px">
22+
<CopyPageButton content={content} />
23+
<ActionMenu slug={slug} />
24+
</HStack>
25+
)
26+
}
27+
28+
const CopyPageButton = (props: { content: string }) => {
29+
const { content } = props
30+
const clipboard = useClipboard({ value: content, timeout: 1000 })
31+
return (
32+
<Button onClick={clipboard.copy} size="xs" variant="outline" borderEndRadius="0">
33+
<HStack gap="2">
34+
{clipboard.copied ? <CheckIcon size={16} /> : <SiMarkdown size={18} />}
35+
<Box as="span" textStyle="sm" fontWeight="medium">
36+
Copy Page
37+
</Box>
38+
</HStack>
39+
</Button>
40+
)
41+
}
42+
43+
const ActionMenu = (props: { slug: string }) => {
44+
const { slug } = props
45+
46+
const pageUrl = getPublicUrl(`/docs/${slug}`)
47+
const readUrl = encodeURIComponent(
48+
`Use web browsing to access links and information: ${pageUrl}\n\nI want to ask some questions`,
49+
)
50+
51+
const items = [
52+
{
53+
label: 'View as markdown',
54+
href: `${pageUrl}.mdx`,
55+
icon: () => <SiMarkdown size={18} />,
56+
},
57+
{
58+
label: 'Open in ChatGPT',
59+
href: `https://chatgpt.com/?hints=search&q=${readUrl}`,
60+
icon: () => (
61+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
62+
<title>ChatGPT</title>
63+
<path d="M22.282 9.821a5.985 5.985 0 0 0-.516-4.91 6.046 6.046 0 0 0-6.51-2.9A6.065 6.065 0 0 0 4.981 4.18a5.985 5.985 0 0 0-3.998 2.9 6.046 6.046 0 0 0 .743 7.097 5.98 5.98 0 0 0 .51 4.911 6.051 6.051 0 0 0 6.515 2.9A5.985 5.985 0 0 0 13.26 24a6.056 6.056 0 0 0 5.772-4.206 5.99 5.99 0 0 0 3.997-2.9 6.056 6.056 0 0 0-.747-7.073zM13.26 22.43a4.476 4.476 0 0 1-2.876-1.04l.141-.081 4.779-2.758a.795.795 0 0 0 .392-.681v-6.737l2.02 1.168a.071.071 0 0 1 .038.052v5.583a4.504 4.504 0 0 1-4.494 4.494zM3.6 18.304a4.47 4.47 0 0 1-.535-3.014l.142.085 4.783 2.759a.771.771 0 0 0 .78 0l5.843-3.369v2.332a.08.08 0 0 1-.033.062L9.74 19.95a4.5 4.5 0 0 1-6.14-1.646zM2.34 7.896a4.485 4.485 0 0 1 2.366-1.973V11.6a.766.766 0 0 0 .388.676l5.815 3.355-2.02 1.168a.076.076 0 0 1-.071 0l-4.83-2.786A4.504 4.504 0 0 1 2.34 7.872zm16.597 3.855l-5.833-3.387L15.119 7.2a.076.076 0 0 1 .071 0l4.83 2.791a4.494 4.494 0 0 1-.676 8.105v-5.678a.79.79 0 0 0-.407-.667zm2.01-3.023l-.141-.085-4.774-2.782a.776.776 0 0 0-.785 0L9.409 9.23V6.897a.066.066 0 0 1 .028-.061l4.83-2.787a4.5 4.5 0 0 1 6.68 4.66zm-12.64 4.135l-2.02-1.164a.08.08 0 0 1-.038-.057V6.075a4.5 4.5 0 0 1 7.375-3.453l-.142.08L8.704 5.46a.795.795 0 0 0-.393.681zm1.097-2.365l2.602-1.5 2.607 1.5v2.999l-2.597 1.5-2.607-1.5z" />
64+
</svg>
65+
),
66+
},
67+
{
68+
label: 'Open in Claude',
69+
href: `https://claude.ai/new?q=${readUrl}`,
70+
icon: () => (
71+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" role="img">
72+
<title>Anthropic</title>
73+
<path d="M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z" />
74+
</svg>
75+
),
76+
},
77+
]
78+
79+
return (
80+
<Menu.Root size="sm" positioning={{ placement: 'bottom-end' }}>
81+
<Menu.Trigger asChild>
82+
<IconButton size="xs" variant="outline" borderStartRadius="0" borderStartWidth="0px">
83+
<ChevronDownIcon size={16} />
84+
</IconButton>
85+
</Menu.Trigger>
86+
<Portal>
87+
<Menu.Positioner>
88+
<Menu.Content minW="200px">
89+
{items.map((item) => {
90+
const Icon = item.icon
91+
return (
92+
<Menu.Item key={item.label} value={item.label} asChild>
93+
<a href={item.href} target="_blank" rel="noopener noreferrer">
94+
<HStack gap="2" width="full">
95+
<Icon />
96+
<span>{item.label}</span>
97+
</HStack>
98+
</a>
99+
</Menu.Item>
100+
)
101+
})}
102+
</Menu.Content>
103+
</Menu.Positioner>
104+
</Portal>
105+
</Menu.Root>
106+
)
107+
}

website/src/content/pages/ai/mcp-server.mdx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
id: mcp-server
33
title: MCP Server
44
description: Bridging the gap between AI agents and Ark UI.
5-
status: new
65
---
76

87
The Ark UI MCP Server is a specialized [Model Context Protocol](https://modelcontextprotocol.io/introduction) server

website/src/content/pages/components/accordion.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,16 @@ exposes the following properties:
118118

119119
## API Reference
120120

121+
### Props
122+
121123
<ComponentTypes id="accordion" />
122124

125+
### Context
126+
127+
These are the properties available when using `Accordion.Context`, `useAccordionContext` hook or `useAccordion` hook.
128+
129+
<ContextType id="accordion" />
130+
123131
## Accessibility
124132

125133
This component complies with the

website/src/content/pages/components/angle-slider.mdx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,13 @@ Use the `step` prop to set the discrete steps of the Angle Slider.
2828

2929
## API Reference
3030

31+
### Props
32+
3133
<ComponentTypes id="angle-slider" />
34+
35+
### Context
36+
37+
These are the properties available when using `AngleSlider.Context`, `useAngleSliderContext` hook or `useAngleSlider`
38+
hook.
39+
40+
<ContextType id="angle-slider" />

0 commit comments

Comments
 (0)