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
2 changes: 2 additions & 0 deletions apps/docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public/sitemap.xml
# llms.txt (generated)
public/llms.txt
public/llms/
# Generated guide markdown files
public/docs/

# Copied examples folder
/examples/
Expand Down
13 changes: 13 additions & 0 deletions apps/docs/DEVELOPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ For a complete run-down on how all of our tools work together, see the main DEVE
4. Visit http://localhost:3001/docs in your browser - don't forget to append the `/docs` to the end
5. Your local site should look exactly like [https://supabase.com/docs](https://supabase.com/docs)

## AI friendly documentation

This project generates Markdown files for each page under `/docs/guides/..` path.

To test locally, within the `apps/docs` directory:

1. Run `pnpm build:guides-markdown`
2. Run `pnpm dev`

This creates Markdown files for all routes under the `public/docs/guides` directory, ignored by Git.

For production this setup runs as a `prebuild` task to allow Vercel to bundle these files with middleware and functions.

## Contributing

For repo organization and style guide, see the [contributing guide](https://github.com/supabase/supabase/blob/master/apps/docs/CONTRIBUTING.md).
22 changes: 22 additions & 0 deletions apps/docs/app/api/guides-md/[...slug]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { promises as fs } from 'fs'
import path from 'path'
import { NextResponse } from 'next/server'

export async function GET(_request: Request, { params }: { params: Promise<{ slug: string[] }> }) {
const { slug } = await params
const baseDir = path.join(process.cwd(), 'public/docs/guides')
const filePath = path.join(baseDir, `${slug.join('/')}.md`)

if (!filePath.startsWith(baseDir + path.sep) && filePath !== baseDir) {
return new NextResponse('Not found', { status: 404 })
}

try {
const content = await fs.readFile(filePath, 'utf-8')
return new NextResponse(content, {
headers: { 'Content-Type': 'text/markdown; charset=utf-8' },
})
} catch {
return new NextResponse('Not found', { status: 404 })
}
}
2 changes: 1 addition & 1 deletion apps/docs/app/guides/local-development/cli/config/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ReactMarkdown from 'react-markdown'
import { CodeBlock } from 'ui'
import { Heading } from 'ui/src/components/CustomHTMLElements'
import { type TOCHeader } from '~/components/GuidesTableOfContents'
import { type TOCHeader } from '~/components/GuidesSidebar'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import type { Parameter } from '~/lib/refGenerator/refTypes'
Expand Down
134 changes: 134 additions & 0 deletions apps/docs/components/GuidesSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
'use client'

import { Check, Copy, ExternalLink } from 'lucide-react'
import { usePathname } from 'next/navigation'
import { useState } from 'react'
import { isFeatureEnabled } from 'common'
import { cn } from 'ui'
import { ExpandableVideo } from 'ui-patterns/ExpandableVideo'
import { Toc, TOCItems, TOCScrollArea } from 'ui-patterns/Toc'
import { Feedback } from '~/components/Feedback'
import { useTocAnchors } from '../features/docs/GuidesMdx.state'

interface TOCHeader {
id?: string
text: string
link: string
level: number
}

function AiTools({ className }: { className?: string }) {
const [copied, setCopied] = useState(false)
let url = ''

// Safe check for server side rendering.
try {
const urlParts = new URL(`${window.location}`)
url = urlParts.origin + urlParts.pathname
} catch (error) {}

async function copyMarkdown() {
const mdUrl = `${url}.md`

try {
const res = await fetch(mdUrl)
const text = await res.text()
await navigator.clipboard.writeText(text)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
} catch (error) {
console.error('Failed to copy markdown', error)
}
}

return (
<section className={cn(className)} aria-labelledby="ask-ai-title">
<h3
id="ask-ai-title"
className="block font-mono uppercase text-xs text-foreground-light mb-3"
>
AI Tools
</h3>
<div className="flex flex-col gap-2">
<button
onClick={copyMarkdown}
className="flex items-center gap-1.5 text-xs text-foreground-lighter hover:text-foreground text-left transition-colors"
>
{copied ? (
<Check size={14} strokeWidth={1.5} className="text-brand" />
) : (
<Copy size={14} strokeWidth={1.5} />
)}
{copied ? 'Copied!' : 'Copy as Markdown'}
</button>
<a
href={`https://chatgpt.com/?hint=search&q=Read from ${url} so I can ask questions about its contents`}
target="_blank"
rel="noreferrer noopener"
className="flex items-center gap-1.5 text-xs text-foreground-lighter hover:text-foreground transition-colors"
>
<ExternalLink size={14} strokeWidth={1.5} />
Ask ChatGPT
</a>
<a
href={`https://claude.ai/new?q=Read from ${url} so I can ask questions about its contents`}
target="_blank"
rel="noreferrer noopener"
className="flex items-center gap-1.5 text-xs text-foreground-lighter hover:text-foreground transition-colors"
>
<ExternalLink size={14} strokeWidth={1.5} />
Ask Claude
</a>
</div>
</section>
)
}

const GuidesSidebar = ({
className,
video,
hideToc,
}: {
className?: string
video?: string
hideToc?: boolean
}) => {
const pathname = usePathname()
const { toc } = useTocAnchors()
const showFeedback = isFeatureEnabled('feedback:docs')
const tocVideoPreview = `https://img.youtube.com/vi/${video}/0.jpg`

return (
<div className={cn('thin-scrollbar overflow-y-auto h-fit', 'px-px', className)}>
<div className="w-full relative border-l flex flex-col gap-6 lg:gap-8 px-2 h-fit">
{video && (
<div className="relative pl-5">
<ExpandableVideo imgUrl={tocVideoPreview} videoId={video} />
</div>
)}
{showFeedback && (
<div className="pl-5">
<Feedback key={pathname} />
</div>
)}
<div className="pl-5">
<AiTools key={pathname} />
</div>
{!hideToc && toc.length !== 0 && (
<Toc className="-ml-[calc(0.25rem+6px)]">
<h3 className="inline-flex items-center gap-1.5 font-mono text-xs uppercase text-foreground pl-[calc(1.5rem+6px)]">
On this page
</h3>
<TOCScrollArea>
<TOCItems items={toc} />
</TOCScrollArea>
</Toc>
)}
</div>
</div>
)
}

export default GuidesSidebar
export { GuidesSidebar }
export type { TOCHeader }
55 changes: 0 additions & 55 deletions apps/docs/components/GuidesTableOfContents.tsx

This file was deleted.

10 changes: 6 additions & 4 deletions apps/docs/content/guides/api/rest/auto-generated-docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ description: 'Supabase provides documentation that updates automatically.'

Supabase generates documentation in the [Dashboard](/dashboard) which updates as you make database changes.

1. Go to the [API](/dashboard/project/_/api) page in the Dashboard.
2. Select any table under **Tables and Views** in the sidebar.
3. Switch between the JavaScript and the cURL docs using the tabs.
1. Go to the [Project Settings](/dashboard/project/_/settings/general) page in the Dashboard.
2. Select Data API -> Docs
3. Select any table under **Tables and Views** in the sidebar.
4. Switch between the JavaScript and the cURL docs using the tabs.
5. You may also select the SUPABASE_KEY to use.

<video width="99%" muted playsInline controls={true}>
<source
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-docs.mp4"
src="https://xguihxuzqibwxjnimxev.supabase.co/storage/v1/object/public/videos/docs/api/api-docs(2).mp4"
type="video/mp4"
/>
</video>
Loading
Loading