diff --git a/.github/workflows/studio-e2e-test.yml b/.github/workflows/studio-e2e-test.yml index cf3ecc38199..be170f07d38 100644 --- a/.github/workflows/studio-e2e-test.yml +++ b/.github/workflows/studio-e2e-test.yml @@ -83,18 +83,30 @@ jobs: if: steps.filter.outputs.studio == 'true' run: rm -rf supabase && pnpm exec supabase init && mkdir supabase/functions + - name: Pre-start diagnostics + run: | + docker ps -a + sudo ss -tlnp | grep 5432 || echo "5432 free" + - name: Start supabase if: steps.filter.outputs.studio == 'true' run: SKIP_ASSET_UPLOAD=1 pnpm run e2e:setup:cli + - name: Failure diagnostics + if: failure() + run: | + docker ps -a + sudo ss -tlnp | grep 5432 + docker logs $(docker ps -aq) 2>&1 || true + - name: Build studio if: steps.filter.outputs.studio == 'true' run: SKIP_ASSET_UPLOAD=1 NODE_ENV=test NODE_OPTIONS="--max-old-space-size=4096" pnpm run build:studio - - name: 🚀 Run Playwright tests against Vercel Preview + - name: 🚀 Run Playwright tests against local studio build if: steps.filter.outputs.studio == 'true' id: playwright - run: pnpm e2e --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} + run: PWTEST_SHARD_WEIGHTS=62:38 pnpm e2e --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }} - name: Upload blob report to GitHub Actions Artifacts if: always() && steps.filter.outputs.studio == 'true' diff --git a/.prettierignore b/.prettierignore index 5f74f6bc0d8..e1ba10c8806 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,6 +8,9 @@ apps/**/out **/supabase/migrations/*.sql apps/www/schema.sql apps/www/public/images/* +# Generated by apps/www/scripts/generateStaticContent.mjs (GitHub discussion bodies) +apps/www/public/changelog.md +apps/www/public/changelog/*.md apps/www/.generated/* apps/docs/**/generated/* apps/docs/examples/* diff --git a/apps/design-system/__registry__/default/block/chart-composed-metrics.tsx b/apps/design-system/__registry__/default/block/chart-composed-metrics.tsx index 361c35a9a45..deab28e4ee2 100644 --- a/apps/design-system/__registry__/default/block/chart-composed-metrics.tsx +++ b/apps/design-system/__registry__/default/block/chart-composed-metrics.tsx @@ -71,7 +71,7 @@ export default function ChartComposedMetrics() { } + loadingState={} > @@ -89,7 +89,7 @@ export default function ChartComposedMetrics() { } + loadingState={} > diff --git a/apps/design-system/__registry__/index.tsx b/apps/design-system/__registry__/index.tsx index 61ccda9e6b0..b442e4c0ecc 100644 --- a/apps/design-system/__registry__/index.tsx +++ b/apps/design-system/__registry__/index.tsx @@ -126,6 +126,17 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, + "admonition-description-only": { + name: "admonition-description-only", + type: "components:example", + registryDependencies: ["admonition"], + component: React.lazy(() => import("@/registry/default/example/admonition-description-only")), + source: "", + files: ["registry/default/example/admonition-description-only.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, "admonition-warning": { name: "admonition-warning", type: "components:example", @@ -137,6 +148,17 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, + "admonition-success": { + name: "admonition-success", + type: "components:example", + registryDependencies: ["admonition"], + component: React.lazy(() => import("@/registry/default/example/admonition-success")), + source: "", + files: ["registry/default/example/admonition-success.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, "admonition-destructive": { name: "admonition-destructive", type: "components:example", @@ -2568,6 +2590,17 @@ export const Index: Record = { subcategory: "undefined", chunks: [] }, + "multi-select-in-dialog": { + name: "multi-select-in-dialog", + type: "components:example", + registryDependencies: undefined, + component: React.lazy(() => import("@/registry/default/example/multi-select-in-dialog")), + source: "", + files: ["registry/default/example/multi-select-in-dialog.tsx"], + category: "undefined", + subcategory: "undefined", + chunks: [] + }, "tree-view-demo": { name: "tree-view-demo", type: "components:example", diff --git a/apps/design-system/app/Providers.tsx b/apps/design-system/app/Providers.tsx index a7fe031c76b..46dce708544 100644 --- a/apps/design-system/app/Providers.tsx +++ b/apps/design-system/app/Providers.tsx @@ -1,20 +1,19 @@ 'use client' +import { ThemeProvider } from 'common' import { Provider as JotaiProvider } from 'jotai' -import { ThemeProvider as NextThemesProvider } from 'next-themes' -import { ThemeProviderProps } from 'next-themes/dist/types' import { TooltipProvider } from 'ui' import { MobileSidebarProvider } from '@/context/mobile-sidebar-context' -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { +export function Providers({ children }: { children: React.ReactNode }) { return ( - + {children} - + ) } diff --git a/apps/design-system/app/globals.css b/apps/design-system/app/globals.css deleted file mode 100644 index f7f07cd6431..00000000000 --- a/apps/design-system/app/globals.css +++ /dev/null @@ -1,29 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* :root { - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - } -} */ - -body { - /* color: rgb(var(--foreground-rgb)); - background: linear-gradient(to bottom, transparent, rgb(var(--background-end-rgb))) - rgb(var(--background-start-rgb)); */ -} -/* -@layer utilities { - .text-balance { - text-wrap: balance; - } -} */ diff --git a/apps/design-system/app/layout.tsx b/apps/design-system/app/layout.tsx index a7d6c82e2dd..69176828eb9 100644 --- a/apps/design-system/app/layout.tsx +++ b/apps/design-system/app/layout.tsx @@ -1,10 +1,10 @@ +import 'react-data-grid/lib/styles.css' import '@/styles/globals.css' -import '../../studio/styles/typography.css' import type { Metadata, Viewport } from 'next' import { customFont, sourceCodePro } from './fonts' -import { ThemeProvider } from './Providers' +import { Providers } from './Providers' import { Toaster } from './toaster' const className = `${customFont.variable} ${sourceCodePro.variable}` @@ -131,16 +131,12 @@ export default async function Layout({ children }: RootLayoutProps) { /> - +
{children}
-
+ ) diff --git a/apps/design-system/components/class-label.tsx b/apps/design-system/components/class-label.tsx index 6575c2f4373..a779b1d353e 100644 --- a/apps/design-system/components/class-label.tsx +++ b/apps/design-system/components/class-label.tsx @@ -5,7 +5,7 @@ const ClassLabel = React.forwardRef {children} diff --git a/apps/design-system/components/code-block-wrapper.tsx b/apps/design-system/components/code-block-wrapper.tsx index 2a6c81ce03e..d4be5c110d3 100644 --- a/apps/design-system/components/code-block-wrapper.tsx +++ b/apps/design-system/components/code-block-wrapper.tsx @@ -36,7 +36,7 @@ export function CodeBlockWrapper({
diff --git a/apps/design-system/components/code-fragment.tsx b/apps/design-system/components/code-fragment.tsx index 4e688bf9c28..ccf33703720 100644 --- a/apps/design-system/components/code-fragment.tsx +++ b/apps/design-system/components/code-fragment.tsx @@ -51,7 +51,7 @@ export function CodeFragment({ return (

Code fragment{' '} - + {name} {' '} not found in registry. @@ -94,10 +94,10 @@ export function CodeFragment({ )} > {showGrid && ( -

+
)} {showDottedGrid && ( -
+
)}
{ComponentPreview}
diff --git a/apps/design-system/components/color-palette.tsx b/apps/design-system/components/color-palette.tsx new file mode 100644 index 00000000000..024b7312f8a --- /dev/null +++ b/apps/design-system/components/color-palette.tsx @@ -0,0 +1,84 @@ +'use client' + +import { useState } from 'react' + +const colorNames = [ + 'Amber', + 'Blue', + 'Brand', + 'Crimson', + 'Gold', + 'Gray', + 'Green', + 'Indigo', + 'Orange', + 'Pink', + 'Purple', + 'Red', + 'Scale', + 'Slate', + 'Tomato', + 'Violet', + 'Yellow', +] + +const SCALE_STEPS = Array.from({ length: 12 }, (_, i) => i + 1) + +const GRID_COLS = 'grid-cols-[6rem_repeat(12,minmax(0,1fr))]' + +const ColorPalette = () => { + const [copied, setCopied] = useState(null) + + const handleCopy = async (value: string) => { + try { + await navigator.clipboard.writeText(value) + setCopied(value) + setTimeout(() => setCopied(null), 1500) + } catch (err) { + console.error('Failed to copy text: ', err) + } + } + + return ( +
+
+
+
+ {SCALE_STEPS.map((step) => ( +
+ {step} +
+ ))} +
+ {colorNames.map((name) => { + const slug = name.toLowerCase() + return ( +
+
{name}
+ {SCALE_STEPS.map((step) => { + const reference = `var(--color-${slug}-${step * 100})` + const isCopied = copied === reference + return ( + + ) + })} +
+ ) + })} +
+
+ ) +} + +export { ColorPalette } diff --git a/apps/design-system/components/colors.tsx b/apps/design-system/components/colors.tsx index d7ba3a364c6..5a0660ac63a 100644 --- a/apps/design-system/components/colors.tsx +++ b/apps/design-system/components/colors.tsx @@ -70,7 +70,7 @@ const Colors = ({ onClick={() => handleCopy(x, i)} > - + {copiedIndex === i ? 'Copied!' : x} diff --git a/apps/design-system/components/command-menu.tsx b/apps/design-system/components/command-menu.tsx index a2dd7b3de58..fbd26bea2e8 100644 --- a/apps/design-system/components/command-menu.tsx +++ b/apps/design-system/components/command-menu.tsx @@ -56,7 +56,7 @@ export function CommandMenu({ ...props }: DialogProps) { diff --git a/apps/design-system/components/component-preview.tsx b/apps/design-system/components/component-preview.tsx index b6c5005fcb1..0189c7ee1ed 100644 --- a/apps/design-system/components/component-preview.tsx +++ b/apps/design-system/components/component-preview.tsx @@ -55,7 +55,7 @@ export function ComponentPreview({ return (

Component{' '} - + {name} {' '} not found in registry. @@ -99,10 +99,10 @@ export function ComponentPreview({ )} > {showGrid && ( -

+
)} {showDottedGrid && ( -
+
)}
{ComponentPreview}
{/*
*/} @@ -144,10 +144,10 @@ export function ComponentPreview({ })} > {showGrid && ( -
+
)} {showDottedGrid && ( -
+
)}
{ComponentPreview}
@@ -164,12 +164,12 @@ export function ComponentPreview({ px-4 py-4 border border-r group - data-[state=closed]:rounded-bl-md data-[state=closed]:rounded-br-md + data-closed:rounded-bl-md data-closed:rounded-br-md `} > View code diff --git a/apps/design-system/components/grid.tsx b/apps/design-system/components/grid.tsx index 659117a7737..23ca11ac432 100644 --- a/apps/design-system/components/grid.tsx +++ b/apps/design-system/components/grid.tsx @@ -47,8 +47,8 @@ const GridItem = forwardRef>( transition group-hover:border group-hover:border-foreground-muted - group-data-[state=open]:border - group-data-[state=open]:border-foreground-muted + group-data-open:border + group-data-open:border-foreground-muted " > {children} diff --git a/apps/design-system/components/icons.tsx b/apps/design-system/components/icons.tsx index e94f408759d..ce07e959a04 100644 --- a/apps/design-system/components/icons.tsx +++ b/apps/design-system/components/icons.tsx @@ -18,11 +18,8 @@ function Icons() { - - + + {icon.name} diff --git a/apps/design-system/components/mdx-components.tsx b/apps/design-system/components/mdx-components.tsx index 5e44875ed9e..d753761ad15 100644 --- a/apps/design-system/components/mdx-components.tsx +++ b/apps/design-system/components/mdx-components.tsx @@ -31,6 +31,7 @@ import { StyleWrapper } from './style-wrapper' import { Callout } from '@/components/callout' import { CodeBlockWrapper } from '@/components/code-block-wrapper' import { CodeFragment } from '@/components/code-fragment' +import { ColorPalette } from '@/components/color-palette' import { Colors } from '@/components/colors' import { ComponentExample } from '@/components/component-example' import { ComponentPreview } from '@/components/component-preview' @@ -92,10 +93,7 @@ const components = { /> ), p: ({ className, ...props }: React.HTMLAttributes) => ( -

+

), ul: ({ className, ...props }: React.HTMLAttributes) => (

    @@ -127,7 +125,7 @@ const components = { th: ({ className, ...props }: React.HTMLAttributes) => ( ) => ( ) => ( ) => ( ), Colors, + ColorPalette, Icons, ThemeSettings, CodeFragment, diff --git a/apps/design-system/components/theme-settings.tsx b/apps/design-system/components/theme-settings.tsx index bb074b6546c..7b72bd9daab 100644 --- a/apps/design-system/components/theme-settings.tsx +++ b/apps/design-system/components/theme-settings.tsx @@ -3,7 +3,7 @@ import { useTheme } from 'next-themes' import { useEffect, useState } from 'react' import SVG from 'react-inlinesvg' -import { RadioGroup_Shadcn_, RadioGroupLargeItem_Shadcn_, singleThemes, Theme } from 'ui' +import { RadioGroup, RadioGroupLargeItem, singleThemes, Theme } from 'ui' const ThemeSettings = () => { const [mounted, setMounted] = useState(false) @@ -25,7 +25,7 @@ const ThemeSettings = () => { function SingleThemeSelection() { return (
    - { className="flex flex-wrap gap-3" > {singleThemes.map((theme: Theme) => ( - + - + ))} - +
    ) } diff --git a/apps/design-system/components/theme-switcher-dropdown.tsx b/apps/design-system/components/theme-switcher-dropdown.tsx index ffd0e320075..86aec9f5b2f 100644 --- a/apps/design-system/components/theme-switcher-dropdown.tsx +++ b/apps/design-system/components/theme-switcher-dropdown.tsx @@ -33,7 +33,7 @@ const ThemeSwitcherDropdown = () => { return null } - const iconClasses = 'text-foreground-lighter group-data-[state=open]:text-foreground' + const iconClasses = 'text-foreground-lighter group-data-open:text-foreground' return ( <> diff --git a/apps/design-system/components/top-navigation.tsx b/apps/design-system/components/top-navigation.tsx index 0af6b28d06b..232167a8528 100644 --- a/apps/design-system/components/top-navigation.tsx +++ b/apps/design-system/components/top-navigation.tsx @@ -13,7 +13,7 @@ export const TopNavigation = () => { const { toggle } = useMobileSidebar() return ( -
    +
-

+

As a result, people stopped telling jokes, and the kingdom fell into a gloom. But there was one person who refused to let the king's foolishness get him down: a court jester named Jokester. @@ -41,12 +41,12 @@ export default function TypographyDemo() {

Jokester's Revolt

-

+

Jokester began sneaking into the castle in the middle of the night and leaving jokes all over the place: under the king's pillow, in his soup, even in the royal toilet. The king was furious, but he couldn't seem to stop Jokester.

-

+

And then, one day, the people of the kingdom discovered that the jokes left by Jokester were so funny that they couldn't help but laugh. And once they started laughing, they couldn't stop. @@ -54,7 +54,7 @@ export default function TypographyDemo() {

The People's Rebellion

-

+

The people of the kingdom, feeling uplifted by the laughter, started to tell jokes and puns again, and soon the entire kingdom was in on the joke.

@@ -62,48 +62,48 @@ export default function TypographyDemo() { - - - - - - - -
+ King's Treasury + People's happiness
+ Empty + Overflowing
+ Modest + Satisfied
+ Full + Ecstatic
-

+

The king, seeing how much happier his subjects were, realized the error of his ways and repealed the joke tax. Jokester was declared a hero, and the kingdom lived happily ever after.

-

+

The moral of the story is: never underestimate the power of a good laugh and always be careful of bad ideas.

diff --git a/apps/design-system/registry/default/example/typography-inline-code.tsx b/apps/design-system/registry/default/example/typography-inline-code.tsx index fbbc96ba688..36e5b271396 100644 --- a/apps/design-system/registry/default/example/typography-inline-code.tsx +++ b/apps/design-system/registry/default/example/typography-inline-code.tsx @@ -1,6 +1,6 @@ export default function TypographyInlineCode() { return ( - + @radix-ui/react-alert-dialog ) diff --git a/apps/design-system/registry/default/example/typography-p.tsx b/apps/design-system/registry/default/example/typography-p.tsx index 0a20c523c48..ed5f1131101 100644 --- a/apps/design-system/registry/default/example/typography-p.tsx +++ b/apps/design-system/registry/default/example/typography-p.tsx @@ -1,6 +1,6 @@ export default function TypographyP() { return ( -

+

The king, seeing how much happier his subjects were, realized the error of his ways and repealed the joke tax.

diff --git a/apps/design-system/registry/default/example/typography-table.tsx b/apps/design-system/registry/default/example/typography-table.tsx index 7a91dfda844..a24e2c5316e 100644 --- a/apps/design-system/registry/default/example/typography-table.tsx +++ b/apps/design-system/registry/default/example/typography-table.tsx @@ -4,36 +4,36 @@ export default function TypographyTable() { - - - - - - - - diff --git a/apps/design-system/registry/examples.ts b/apps/design-system/registry/examples.ts index e18429c0951..d5f9a19ae22 100644 --- a/apps/design-system/registry/examples.ts +++ b/apps/design-system/registry/examples.ts @@ -25,12 +25,24 @@ export const examples: Registry = [ registryDependencies: ['admonition'], files: ['example/admonition-button.tsx'], }, + { + name: 'admonition-description-only', + type: 'components:example', + registryDependencies: ['admonition'], + files: ['example/admonition-description-only.tsx'], + }, { name: 'admonition-warning', type: 'components:example', registryDependencies: ['admonition'], files: ['example/admonition-warning.tsx'], }, + { + name: 'admonition-success', + type: 'components:example', + registryDependencies: ['admonition'], + files: ['example/admonition-success.tsx'], + }, { name: 'admonition-destructive', type: 'components:example', @@ -1418,6 +1430,11 @@ export const examples: Registry = [ type: 'components:example', files: ['example/multi-select-inline-search-input.tsx'], }, + { + name: 'multi-select-in-dialog', + type: 'components:example', + files: ['example/multi-select-in-dialog.tsx'], + }, { name: 'tree-view-demo', type: 'components:example', diff --git a/apps/design-system/styles/globals.css b/apps/design-system/styles/globals.css index c0a8ac60719..0418cb7fedf 100644 --- a/apps/design-system/styles/globals.css +++ b/apps/design-system/styles/globals.css @@ -1,11 +1,18 @@ -@import './../../../packages/ui/build/css/source/global.css'; -@import './../../../packages/ui/build/css/themes/dark.css'; +@import 'config/tailwind.config.css'; @import './../../../packages/ui/build/css/themes/classic-dark.css'; -@import './../../../packages/ui/build/css/themes/light.css'; +@import 'config/typography.css'; -@tailwind base; -@tailwind components; -@tailwind utilities; +@source '../app/**/*.{js,ts,jsx,tsx}'; +@source '../components/**/*.{js,ts,jsx,tsx}'; +@source '../registry/**/*.{js,ts,jsx,tsx}'; +@source '../content/**/*.mdx'; +@source './../../../packages/ui/src/**/*.{tsx,ts,js}'; +@source './../../../packages/ui-patterns/src/**/*.{tsx,ts,js}'; + +@theme { + /* added to get max-w-site */ + --container-site: 128rem; +} @layer base { :root { @@ -33,7 +40,7 @@ @apply scroll-smooth; } body { - @apply bg text-foreground; + @apply bg-default text-foreground; /* font-feature-settings: "rlig" 1, "calt" 1; */ font-synthesis-weight: none; text-rendering: optimizeLegibility; @@ -68,3 +75,7 @@ @apply px-4; } } + +.rdg-cell { + padding-inline: 0.5rem; +} diff --git a/apps/design-system/styles/mdx.css b/apps/design-system/styles/mdx.css index 84391167b6f..32f049edca0 100644 --- a/apps/design-system/styles/mdx.css +++ b/apps/design-system/styles/mdx.css @@ -1,3 +1,5 @@ +@reference './globals.css'; + /* [data-theme="light"] { display: block; } */ diff --git a/apps/design-system/tailwind.config.js b/apps/design-system/tailwind.config.js deleted file mode 100644 index 4d875930e75..00000000000 --- a/apps/design-system/tailwind.config.js +++ /dev/null @@ -1,21 +0,0 @@ -const config = require('config/tailwind.config') - -module.exports = config({ - content: [ - './app/**/*.{js,ts,jsx,tsx}', - './components/**/*.{js,ts,jsx,tsx}', - './registry/**/*.{js,ts,jsx,tsx}', - // purge styles from grid library - // - './../../packages/ui/src/**/*.{tsx,ts,js}', - './../../packages/ui-patterns/src/**/*.{tsx,ts,js}', - ], - plugins: [require('@tailwindcss/container-queries')], - theme: { - extend: { - maxWidth: { - site: '128rem', - }, - }, - }, -}) diff --git a/apps/docs/app/api/guides-md/[...slug]/route.ts b/apps/docs/app/api/guides-md/[...slug]/route.ts index b942ff2f73c..3e877b3181f 100644 --- a/apps/docs/app/api/guides-md/[...slug]/route.ts +++ b/apps/docs/app/api/guides-md/[...slug]/route.ts @@ -1,14 +1,15 @@ -import { promises as fs } from 'fs' -import path from 'path' +import { promises as fs } from 'node:fs' +import path from 'node:path' +import { BASE_PATH, IS_PRODUCTION, PROD_URL } from '~/lib/constants' import { NextResponse } from 'next/server' -export async function GET(_request: Request, { params }: { params: Promise<{ slug: string[] }> }) { +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 }) + return notFoundResponse(request, slug) } try { @@ -20,6 +21,86 @@ export async function GET(_request: Request, { params }: { params: Promise<{ slu }, }) } catch { - return new NextResponse('Not found', { status: 404 }) + return notFoundResponse(request, slug) } } + +/** + * Resolves the docs base URL from trusted sources only. + * Avoids host-header-driven SSRF from request origin. + */ +function resolveDocsBaseUrl() { + if (IS_PRODUCTION) return PROD_URL + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}${BASE_PATH}` + if (process.env.NEXT_PUBLIC_SITE_URL) return `${process.env.NEXT_PUBLIC_SITE_URL}${BASE_PATH}` + return undefined +} + +async function notFoundResponse(_request: Request, slug: string[]) { + const baseUrl = resolveDocsBaseUrl() + const query = slug.join(' ').replace(/[_-]/g, ' ') + const suggestions = baseUrl ? await searchForSuggestions(baseUrl, query) : [] + const markdown = buildNotFoundMarkdown(slug, suggestions) + return new NextResponse(markdown, { + status: 404, + headers: { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'no-store', + }, + }) +} + +interface SearchSuggestion { + title: string + href: string +} + +async function searchForSuggestions(baseUrl: string, query: string): Promise { + try { + const response = await fetch(`${baseUrl}/api/graphql`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + query: ` + query SearchDocs($query: String!) { + searchDocs(query: $query, limit: 5) { + nodes { + title + href + } + } + } + `, + variables: { query }, + }), + }) + + if (!response.ok) return [] + + const data = await response.json() + return (data?.data?.searchDocs?.nodes ?? []).filter( + (n: Partial): n is SearchSuggestion => !!n.title && !!n.href + ) + } catch { + return [] + } +} + +function buildNotFoundMarkdown(slug: string[], suggestions: SearchSuggestion[]): string { + const pagePath = slug.join('/') + + const suggestionsMd = + suggestions.length > 0 + ? `## You might be looking for...\n\n${suggestions + .map(({ title, href }) => `- [${title}](${href}.md)`) + .join('\n')}\n` + : '' + + return `# 404 - Page Not Found + +The page \`/docs/guides/${pagePath}.md\` does not exist. + +${suggestionsMd} +See also: [Changelog](https://supabase.com/changelog.md) +` +} diff --git a/apps/docs/app/graphiql/page.tsx b/apps/docs/app/graphiql/page.tsx index 671b5525963..377685efd69 100644 --- a/apps/docs/app/graphiql/page.tsx +++ b/apps/docs/app/graphiql/page.tsx @@ -29,5 +29,5 @@ export default function GraphiQLPage() { notFound() } - return + return } diff --git a/apps/docs/app/guides/ai-tools/[[...slug]]/page.tsx b/apps/docs/app/guides/ai-tools/[[...slug]]/page.tsx new file mode 100644 index 00000000000..e23f1811e35 --- /dev/null +++ b/apps/docs/app/guides/ai-tools/[[...slug]]/page.tsx @@ -0,0 +1,26 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' +import { + genGuideMeta, + genGuidesStaticParams, + getGuidesMarkdown, +} from '~/features/docs/GuidesMdx.utils' +import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' + +type Params = { slug?: string[] } + +const AiToolsGuidePage = async (props: { params: Promise }) => { + const params = await props.params + const slug = ['ai-tools', ...(params.slug ?? [])] + const data = await getGuidesMarkdown(slug) + + return +} + +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('ai-tools') : getEmptyArray +const generateMetadata = genGuideMeta((params: { slug?: string[] }) => + getGuidesMarkdown(['ai-tools', ...(params.slug ?? [])]) +) + +export default AiToolsGuidePage +export { generateMetadata, generateStaticParams } diff --git a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/page.tsx b/apps/docs/app/guides/ai-tools/ai-prompts/[slug]/page.tsx similarity index 92% rename from apps/docs/app/guides/getting-started/ai-prompts/[slug]/page.tsx rename to apps/docs/app/guides/ai-tools/ai-prompts/[slug]/page.tsx index 82f3837575d..7eaf8f3ceae 100644 --- a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/page.tsx +++ b/apps/docs/app/guides/ai-tools/ai-prompts/[slug]/page.tsx @@ -1,13 +1,13 @@ -import { source } from 'common-tags' -import { notFound } from 'next/navigation' -import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { generateAiPromptMetadata, generateAiPromptsStaticParams, generateCursorPromptDeepLink, getAiPrompt, wrapInMarkdownCodeBlock, -} from './AiPrompts.utils' +} from '~/app/guides/getting-started/ai-prompts/[slug]/AiPrompts.utils' +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { source } from 'common-tags' +import { notFound } from 'next/navigation' export const dynamicParams = false @@ -50,6 +50,7 @@ export default async function AiPromptsPage(props: { params: Promise<{ slug: str meta={{ title: `AI Prompt: ${heading}` }} content={content} editLink={newEditLink(`supabase/supabase/blob/master/examples/prompts/${slug}.md`)} + pathname={`/guides/ai-tools/ai-prompts/${slug}`} /> ) } diff --git a/apps/docs/app/guides/ai-tools/layout.tsx b/apps/docs/app/guides/ai-tools/layout.tsx new file mode 100644 index 00000000000..187abe5919e --- /dev/null +++ b/apps/docs/app/guides/ai-tools/layout.tsx @@ -0,0 +1,5 @@ +import Layout from '~/layouts/guides' + +export default async function AiToolsLayout({ children }: { children: React.ReactNode }) { + return {children} +} diff --git a/apps/docs/app/guides/ai-tools/troubleshooting/page.tsx b/apps/docs/app/guides/ai-tools/troubleshooting/page.tsx new file mode 100644 index 00000000000..573b36fb795 --- /dev/null +++ b/apps/docs/app/guides/ai-tools/troubleshooting/page.tsx @@ -0,0 +1,15 @@ +import SectionTroubleshootingPage, { + generateSectionTroubleshootingMetadata, +} from '~/features/docs/TroubleshootingSection.page' + +export default async function AiToolsTroubleshootingPage() { + return ( + + ) +} + +export const metadata = generateSectionTroubleshootingMetadata('AI', 'AI Tools') diff --git a/apps/docs/app/guides/ai/[[...slug]]/page.tsx b/apps/docs/app/guides/ai/[[...slug]]/page.tsx index f517756dd71..c17bd51fb12 100644 --- a/apps/docs/app/guides/ai/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/ai/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const AiGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('ai') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('ai') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['ai', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/api/[[...slug]]/page.tsx b/apps/docs/app/guides/api/[[...slug]]/page.tsx index f68aadb62e4..1ec826244b8 100644 --- a/apps/docs/app/guides/api/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/api/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const ApiGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('api') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('api') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['api', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/auth/[[...slug]]/page.tsx b/apps/docs/app/guides/auth/[[...slug]]/page.tsx index 49c670824a4..f4c062505be 100644 --- a/apps/docs/app/guides/auth/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/auth/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const AuthGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('auth') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('auth') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['auth', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/cron/[[...slug]]/page.tsx b/apps/docs/app/guides/cron/[[...slug]]/page.tsx index e77e622d824..3c40eef45c9 100644 --- a/apps/docs/app/guides/cron/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/cron/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const CronGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('cron') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('cron') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['cron', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/database/[[...slug]]/page.tsx b/apps/docs/app/guides/database/[[...slug]]/page.tsx index 8b6622d22fc..124f0b315a9 100644 --- a/apps/docs/app/guides/database/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/database/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const DatabaseGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('database') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('database') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['database', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/database/database-advisors/page.tsx b/apps/docs/app/guides/database/database-advisors/page.tsx index 334be03cd77..e35e9da410c 100644 --- a/apps/docs/app/guides/database/database-advisors/page.tsx +++ b/apps/docs/app/guides/database/database-advisors/page.tsx @@ -1,19 +1,18 @@ +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' +import { MDXRemoteBase } from '~/features/docs/MdxBase' +import { TabPanel, Tabs } from '~/features/ui/Tabs' +import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' +import { getGitHubFileContents, octokit, OCTOKIT_RETRY_OPTIONS } from '~/lib/octokit' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' import { capitalize } from 'lodash-es' import rehypeSlug from 'rehype-slug' import { Heading } from 'ui' import { Admonition } from 'ui-patterns' -import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' -import { MDXRemoteBase } from '~/features/docs/MdxBase' -import { OCTOKIT_RETRY_OPTIONS, getGitHubFileContents, octokit } from '~/lib/octokit' -import { TabPanel, Tabs } from '~/features/ui/Tabs' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' -import { SerializeOptions } from '~/types/next-mdx-remote-serialize' - // We fetch these docs at build time from an external repo const org = 'supabase' const repo = 'splinter' @@ -64,7 +63,7 @@ const DatabaseAdvisorDocs = async () => { } as SerializeOptions return ( - + Available checks @@ -87,7 +86,7 @@ const DatabaseAdvisorDocs = async () => { . ) : ( - + {lints.map((lint) => ( { } const generateStaticParams = async () => { - if (IS_PROD) { + if (!IS_DEV) { return [] } diff --git a/apps/docs/app/guides/deployment/[[...slug]]/page.tsx b/apps/docs/app/guides/deployment/[[...slug]]/page.tsx index 7b8be32adcc..0b15eb80b1f 100644 --- a/apps/docs/app/guides/deployment/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/deployment/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const DeploymentGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('deployment') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('deployment') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['deployment', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx b/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx index c97957170c2..07c76c8da40 100644 --- a/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/deployment/terraform/[[...slug]]/page.tsx @@ -1,24 +1,24 @@ +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' +import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' +import { isValidGuideFrontmatter } from '~/lib/docs' +import { linkTransform, UrlTransformFunction } from '~/lib/mdx/plugins/rehypeLinkTransform' +import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' +import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' +import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' +import { getGitHubFileContents } from '~/lib/octokit' +import { SerializeOptions } from '~/types/next-mdx-remote-serialize' import matter from 'gray-matter' import { notFound } from 'next/navigation' import rehypeSlug from 'rehype-slug' -import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' -import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils' -import { getGitHubFileContents } from '~/lib/octokit' -import { isValidGuideFrontmatter } from '~/lib/docs' -import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' -import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' -import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' -import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' import { terraformDocsBranch, terraformDocsDocsDir, terraformDocsOrg, terraformDocsRepo, } from '../terraformConstants' -import { SerializeOptions } from '~/types/next-mdx-remote-serialize' -import { IS_PROD } from 'common' -import { getEmptyArray } from '~/features/helpers.fn' // Each external docs page is mapped to a local page const pageMap = [ @@ -143,7 +143,7 @@ const getContent = async ({ slug }: Params) => { } } -const generateStaticParams = IS_PROD +const generateStaticParams = !IS_DEV ? async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] })) : getEmptyArray const generateMetadata = genGuideMeta(getContent) diff --git a/apps/docs/app/guides/deployment/terraform/reference/page.tsx b/apps/docs/app/guides/deployment/terraform/reference/page.tsx index e4515a05839..220d551479a 100644 --- a/apps/docs/app/guides/deployment/terraform/reference/page.tsx +++ b/apps/docs/app/guides/deployment/terraform/reference/page.tsx @@ -156,7 +156,7 @@ function Resources({ schema }: { schema: any }) {
  • {nestedAttribute}
      -
    • +
    • { schema[resource].block.attributes[attribute].nested_type @@ -285,7 +285,7 @@ function DataSources({ schema }: { schema: any }) {
    • {nestedAttribute}
        -
      • +
      • { schema[dataSource].block.attributes[attribute].nested_type @@ -352,7 +352,11 @@ const TerraformReferencePage = async () => { const editLink = newEditLink('supabase/terraform-provider-supabase') return ( - + The Terraform Provider provides access to{' '} }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('functions') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('functions') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['functions', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/getting-started/[[...slug]]/page.tsx b/apps/docs/app/guides/getting-started/[[...slug]]/page.tsx index 52e9425c92f..a3c33a9a546 100644 --- a/apps/docs/app/guides/getting-started/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/getting-started/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const GettingStartedGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('getting-started') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('getting-started') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['getting-started', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx b/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx index ed32afb8c67..6e6bf5b3053 100644 --- a/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx +++ b/apps/docs/app/guides/getting-started/ai-prompts/[slug]/AiPromptsIndex.tsx @@ -1,5 +1,4 @@ import Link from 'next/link' - import { GlassPanel } from 'ui-patterns' import { getAiPrompts } from './AiPrompts.utils' @@ -12,7 +11,7 @@ export async function AiPromptsIndex() { {prompts.map((prompt) => ( { } } -const generateStaticParams = IS_PROD +const generateStaticParams = !IS_DEV ? async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] })) : getEmptyArray const generateMetadata = genGuideMeta(getContent) diff --git a/apps/docs/app/guides/integrations/[[...slug]]/page.tsx b/apps/docs/app/guides/integrations/[[...slug]]/page.tsx index 819c1e0bfd7..cc02780dc73 100644 --- a/apps/docs/app/guides/integrations/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/integrations/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const IntegrationsGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('integrations') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('integrations') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['integrations', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/local-development/[[...slug]]/page.tsx b/apps/docs/app/guides/local-development/[[...slug]]/page.tsx index 4166055f268..81dcba599a6 100644 --- a/apps/docs/app/guides/local-development/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/local-development/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const LocalDevelopmentGuidePage = async (props: { params: Promise }) => return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('local-development') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('local-development') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['local-development', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/local-development/cli/config/page.tsx b/apps/docs/app/guides/local-development/cli/config/page.tsx index 75a28a947e0..124ade3a74c 100644 --- a/apps/docs/app/guides/local-development/cli/config/page.tsx +++ b/apps/docs/app/guides/local-development/cli/config/page.tsx @@ -36,7 +36,7 @@ const Config = () => { const editLink = newEditLink('supabase/supabase/blob/master/apps/docs/spec/cli_v1_config.yaml') return ( - + {specFile.info.description}
        {content}
        diff --git a/apps/docs/app/guides/platform/[[...slug]]/page.tsx b/apps/docs/app/guides/platform/[[...slug]]/page.tsx index 7ca75717816..dce62f3cf79 100644 --- a/apps/docs/app/guides/platform/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/platform/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const PlatformGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('platform') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('platform') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['platform', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/queues/[[...slug]]/page.tsx b/apps/docs/app/guides/queues/[[...slug]]/page.tsx index 991c81c189b..fa3852979b4 100644 --- a/apps/docs/app/guides/queues/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/queues/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const QueuesGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('queues') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('queues') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['queues', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/realtime/[[...slug]]/page.tsx b/apps/docs/app/guides/realtime/[[...slug]]/page.tsx index c65eb2ae994..8b9f7bbbce4 100644 --- a/apps/docs/app/guides/realtime/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/realtime/[[...slug]]/page.tsx @@ -1,4 +1,3 @@ -import { IS_PROD } from 'common' import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { genGuideMeta, @@ -6,6 +5,7 @@ import { getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const RealtimeGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('realtime') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('realtime') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['realtime', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/resources/[[...slug]]/page.tsx b/apps/docs/app/guides/resources/[[...slug]]/page.tsx index c4ef4796b42..d32333d1b97 100644 --- a/apps/docs/app/guides/resources/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/resources/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const ResourcesGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('resources') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('resources') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['resources', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/security/[[...slug]]/page.tsx b/apps/docs/app/guides/security/[[...slug]]/page.tsx index 4a62d58281d..ce65389dc74 100644 --- a/apps/docs/app/guides/security/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/security/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const SecurityGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('security') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('security') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['security', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/self-hosting/[[...slug]]/page.tsx b/apps/docs/app/guides/self-hosting/[[...slug]]/page.tsx index 39f7d03b452..9d17d547ad6 100644 --- a/apps/docs/app/guides/self-hosting/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/self-hosting/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const SelfHostingGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('self-hosting') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('self-hosting') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['self-hosting', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/self-hosting/analytics/config/page.tsx b/apps/docs/app/guides/self-hosting/analytics/config/page.tsx index 5874b2b40dd..2c28fa2bf44 100644 --- a/apps/docs/app/guides/self-hosting/analytics/config/page.tsx +++ b/apps/docs/app/guides/self-hosting/analytics/config/page.tsx @@ -1,6 +1,6 @@ import Param from '~/components/Params' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { MDXRemoteBase } from '~/features/docs/MdxBase' import specAnalyticsV0 from '~/spec/analytics_v0_config.yaml' with { type: 'yml' } @@ -23,6 +23,7 @@ const AnalyticsConfigPage = async () => { editLink={newEditLink( 'supabase/supabase/blob/master/apps/docs/app/guides/self-hosting/analytics/config/page.tsx' )} + pathname="/guides/self-hosting/analytics/config" > diff --git a/apps/docs/app/guides/self-hosting/auth/config/page.tsx b/apps/docs/app/guides/self-hosting/auth/config/page.tsx index 4ba779c8304..4221f2f7f42 100644 --- a/apps/docs/app/guides/self-hosting/auth/config/page.tsx +++ b/apps/docs/app/guides/self-hosting/auth/config/page.tsx @@ -1,6 +1,6 @@ import Param from '~/components/Params' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { MDXRemoteBase } from '~/features/docs/MdxBase' import specAuthV1 from '~/spec/gotrue_v1_config.yaml' with { type: 'yml' } @@ -23,6 +23,7 @@ const AuthConfigPage = async () => { editLink={newEditLink( 'supabase/supabase/blob/master/apps/docs/app/guides/self-hosting/auth/config/page.tsx' )} + pathname="/guides/self-hosting/auth/config" > diff --git a/apps/docs/app/guides/self-hosting/realtime/config/page.tsx b/apps/docs/app/guides/self-hosting/realtime/config/page.tsx index 6d3ce48ccaf..80076742be8 100644 --- a/apps/docs/app/guides/self-hosting/realtime/config/page.tsx +++ b/apps/docs/app/guides/self-hosting/realtime/config/page.tsx @@ -1,6 +1,6 @@ import Param from '~/components/Params' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { MDXRemoteBase } from '~/features/docs/MdxBase' import specRealtimeV0 from '~/spec/realtime_v0_config.yaml' with { type: 'yml' } @@ -23,6 +23,7 @@ const RealtimeConfigPage = async () => { editLink={newEditLink( 'supabase/supabase/blob/master/apps/docs/app/guides/self-hosting/realtime/config/page.tsx' )} + pathname="/guides/self-hosting/realtime/config" > diff --git a/apps/docs/app/guides/self-hosting/storage/config/page.tsx b/apps/docs/app/guides/self-hosting/storage/config/page.tsx index 88c71034cd9..c6682ce7add 100644 --- a/apps/docs/app/guides/self-hosting/storage/config/page.tsx +++ b/apps/docs/app/guides/self-hosting/storage/config/page.tsx @@ -1,6 +1,6 @@ import Param from '~/components/Params' -import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { MDXRemoteBase } from '~/features/docs/MdxBase' import specStorageV0 from '~/spec/storage_v0_config.yaml' with { type: 'yml' } @@ -23,6 +23,7 @@ const StorageConfigPage = async () => { editLink={newEditLink( 'supabase/supabase/blob/master/apps/docs/app/guides/self-hosting/storage/config/page.tsx' )} + pathname="/guides/self-hosting/storage/config" > diff --git a/apps/docs/app/guides/storage/[[...slug]]/page.tsx b/apps/docs/app/guides/storage/[[...slug]]/page.tsx index 67ad5d42301..6f01587f27f 100644 --- a/apps/docs/app/guides/storage/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/storage/[[...slug]]/page.tsx @@ -1,4 +1,3 @@ -import { IS_PROD } from 'common' import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { genGuideMeta, @@ -6,6 +5,7 @@ import { getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const StorageGuidePage = async (props: { params: Promise }) => { return } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('storage') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('storage') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['storage', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/telemetry/[[...slug]]/page.tsx b/apps/docs/app/guides/telemetry/[[...slug]]/page.tsx index 26be534bde8..0aa9feceb0d 100644 --- a/apps/docs/app/guides/telemetry/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/telemetry/[[...slug]]/page.tsx @@ -1,11 +1,11 @@ +import { GuideTemplate } from '~/features/docs/GuidesMdx.template' import { - getGuidesMarkdown, genGuideMeta, genGuidesStaticParams, + getGuidesMarkdown, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate } from '~/features/docs/GuidesMdx.template' -import { IS_PROD } from 'common' import { getEmptyArray } from '~/features/helpers.fn' +import { IS_DEV } from '~/lib/constants' type Params = { slug?: string[] } @@ -17,7 +17,7 @@ const MonitoringTroubleshootingGuidePage = async (props: { params: Promise } -const generateStaticParams = IS_PROD ? genGuidesStaticParams('telemetry') : getEmptyArray +const generateStaticParams = !IS_DEV ? genGuidesStaticParams('telemetry') : getEmptyArray const generateMetadata = genGuideMeta((params: { slug?: string[] }) => getGuidesMarkdown(['telemetry', ...(params.slug ?? [])]) ) diff --git a/apps/docs/app/guides/troubleshooting/page.tsx b/apps/docs/app/guides/troubleshooting/page.tsx index 74582ef4838..d00356c6e27 100644 --- a/apps/docs/app/guides/troubleshooting/page.tsx +++ b/apps/docs/app/guides/troubleshooting/page.tsx @@ -26,7 +26,7 @@ export default async function GlobalTroubleshootingPage() { const errors = await getAllTroubleshootingErrors() return ( - +

        Troubleshooting

        diff --git a/apps/docs/app/layout.tsx b/apps/docs/app/layout.tsx index e470d95e69b..aea0eb429ee 100644 --- a/apps/docs/app/layout.tsx +++ b/apps/docs/app/layout.tsx @@ -1,8 +1,7 @@ import '@code-hike/mdx/styles.css' import 'config/code-hike.css' import 'ui-patterns/ShimmeringLoader/index.css' -import '../styles/main.css' -import '../styles/new-docs.css' +import '../styles/globals.css' import '../styles/prism-okaidia.css' import { GlobalProviders } from '~/features/app.providers' diff --git a/apps/docs/app/page.tsx b/apps/docs/app/page.tsx index 337d9c7cc2b..de63965fbd0 100644 --- a/apps/docs/app/page.tsx +++ b/apps/docs/app/page.tsx @@ -29,7 +29,10 @@ const generateMetadata = async (_, parent: ResolvingMetadata): Promise ...(parentAlternates && { languages: parentAlternates.languages || undefined, media: parentAlternates.media || undefined, - types: parentAlternates.types || undefined, + types: { + ...(parentAlternates.types ?? {}), + 'text/markdown': '/llms-full.txt', + }, }), }, } @@ -177,10 +180,16 @@ const clientLibraries = [ const additionalResources = [ { - title: 'Management API', - description: 'Manage your Supabase projects and organizations.', - icon: 'reference-api', - href: '/reference/api/introduction', + title: 'AI tools', + description: 'Develop with Supabase AI-first using plugins, MCP, and skills.', + icon: 'ai-tools', + href: '/guides/ai', + }, + { + title: 'Platform guides', + description: 'Learn more about the tools and services powering Supabase.', + icon: 'platform', + href: '/guides/platform', }, { title: 'Supabase CLI', @@ -189,10 +198,10 @@ const additionalResources = [ href: '/reference/cli/introduction', }, { - title: 'Platform Guides', - description: 'Learn more about the tools and services powering Supabase.', - icon: 'platform', - href: '/guides/platform', + title: 'Management API', + description: 'Manage your Supabase projects and organizations.', + icon: 'reference-api', + href: '/reference/api/introduction', }, { title: 'Integrations', diff --git a/apps/docs/components/AppleSecretGenerator/AppleSecretGenerator.tsx b/apps/docs/components/AppleSecretGenerator/AppleSecretGenerator.tsx index 04936737eba..aeb45ed4bb8 100644 --- a/apps/docs/components/AppleSecretGenerator/AppleSecretGenerator.tsx +++ b/apps/docs/components/AppleSecretGenerator/AppleSecretGenerator.tsx @@ -1,6 +1,8 @@ import { useState } from 'react' -import { Button, Input } from 'ui' +import { Button, Input_Shadcn_ as Input } from 'ui' import { Admonition } from 'ui-patterns/admonition' +import { Input as DataInput } from 'ui-patterns/DataInputs/Input' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' function base64URL(value: string) { return globalThis.btoa(value).replace(/[=]/g, '').replace(/[+]/g, '-').replace(/[\/]/g, '_') @@ -105,30 +107,45 @@ const AppleSecretGenerator = () => { return (

        - setTeamID(e.target.value.trim())} - /> - + setTeamID(e.target.value.trim())} + /> + + setServiceID(e.target.value.trim())} - /> - + setServiceID(e.target.value.trim())} + /> + + setKeyID(e.target.value.trim())} - /> + > + setKeyID(e.target.value.trim())} + /> +
        { {secretKey && ( <>
        - + description={`Valid until: ${expiresAt}. Make sure you generate a new one before then!`} + > + + )}
        diff --git a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx index 357b37ffa77..9e634a825d2 100644 --- a/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx +++ b/apps/docs/components/AuthSmsProviderConfig/AuthSmsProviderConfig.tsx @@ -62,18 +62,18 @@ const AuthSmsProviderConfig = () => { > {selectedProvider && ( { evt.preventDefault() headingRef.current?.focus() }} > - + {selectedProvider.name} - + {selectedProvider.name.toLowerCase().includes('messagebird') && } {selectedProvider.name.toLowerCase().includes('twilio') && } {selectedProvider.name.toLowerCase().includes('vonage') && } diff --git a/apps/docs/components/BodyContentTypeDropdown.tsx b/apps/docs/components/BodyContentTypeDropdown.tsx index 46c5437272e..33334ae094d 100644 --- a/apps/docs/components/BodyContentTypeDropdown.tsx +++ b/apps/docs/components/BodyContentTypeDropdown.tsx @@ -26,7 +26,7 @@ const BodyContentTypeDropdown = ({ types, onSelect }: IParamProps) => { border hover:border-control hover:bg-overlay-hover - border-control px-2 h-[32px] rounded + border-control px-2 h-[32px] rounded-sm font-mono flex items-center gap-1 text-foreground text-sm group-hover:text-foreground transition diff --git a/apps/docs/components/Breadcrumbs.tsx b/apps/docs/components/Breadcrumbs.tsx index b9bde64e769..3e524100491 100644 --- a/apps/docs/components/Breadcrumbs.tsx +++ b/apps/docs/components/Breadcrumbs.tsx @@ -1,18 +1,18 @@ 'use client' +import { resolveBreadcrumbs } from '~/lib/breadcrumbs' +import { useBreakpoint } from 'common' import Link from 'next/link' import { usePathname, useSearchParams } from 'next/navigation' import React, { Fragment, Suspense } from 'react' - -import { useBreakpoint } from 'common' import { Breadcrumb_Shadcn_ as Breadcrumb, - BreadcrumbList_Shadcn_ as BreadcrumbList, + BreadcrumbEllipsis_Shadcn_ as BreadcrumbEllipsis, BreadcrumbItem_Shadcn_ as BreadcrumbItem, BreadcrumbLink_Shadcn_ as BreadcrumbLink, - BreadcrumbSeparator_Shadcn_ as BreadcrumbSeparator, + BreadcrumbList_Shadcn_ as BreadcrumbList, BreadcrumbPage_Shadcn_ as BreadcrumbPage, - BreadcrumbEllipsis_Shadcn_ as BreadcrumbEllipsis, + BreadcrumbSeparator_Shadcn_ as BreadcrumbSeparator, Button, cn, Drawer, @@ -26,9 +26,6 @@ import { DropdownMenuTrigger, } from 'ui' -import * as NavItems from '~/components/Navigation/NavigationMenu/NavigationMenu.constants' -import { getMenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu.utils' - interface BreadcrumbsProps extends React.HTMLAttributes { minLength?: number forceDisplayOnMobile?: boolean @@ -159,56 +156,5 @@ const BreadcrumbsInternal = ({ function useBreadcrumbs() { const pathname = usePathname() - - const isTroubleshootingPage = pathname.startsWith('/guides/troubleshooting') - if (isTroubleshootingPage) { - const breadcrumbs = [{ name: 'Troubleshooting', url: '/guides/troubleshooting' }] - return breadcrumbs - } - - const isAiPromptsPage = pathname.startsWith('/guides/getting-started/ai-prompts') - if (isAiPromptsPage) { - const breadcrumbs = [ - { name: 'Getting started', url: '/guides/getting-started' }, - { name: 'AI Tools' }, - { name: 'Prompts', url: '/guides/getting-started/ai-prompts' }, - ] - return breadcrumbs - } - - // TODO: Breadcrumbs currently can't infer the "AI Tools" parent for /guides/getting-started/ai-* routes, - // so we special-case these paths here. Remove when Breadcrumbs can derive this hierarchy from NavigationMenu. - const isAiSkillsPage = pathname.startsWith('/guides/getting-started/ai-skills') - if (isAiSkillsPage) { - const breadcrumbs = [ - { name: 'Getting started', url: '/guides/getting-started' }, - { name: 'AI Tools' }, - { name: 'Agent Skills', url: '/guides/getting-started/ai-skills' }, - ] - return breadcrumbs - } - - const menuId = getMenuId(pathname) - const menu = NavItems[menuId] - return findMenuItemByUrl(menu, pathname, []) -} - -function findMenuItemByUrl(menu: any, targetUrl: string, parents: any[] = []) { - // If the menu has items, recursively search through them - if (menu.items) { - for (let item of menu.items) { - const result = findMenuItemByUrl(item, targetUrl, [...parents, menu]) - if (result) { - return result - } - } - } - - // Check if the current menu object itself has the target URL - if (menu.url === targetUrl) { - return [...parents, menu] - } - - // If the URL is not found, return null - return null + return resolveBreadcrumbs(pathname) } diff --git a/apps/docs/components/ButtonCard.tsx b/apps/docs/components/ButtonCard.tsx index ecc50fd15c0..f7df15298f3 100644 --- a/apps/docs/components/ButtonCard.tsx +++ b/apps/docs/components/ButtonCard.tsx @@ -26,7 +26,7 @@ const ButtonCard: FC = ({ handleVote('no')} @@ -182,7 +182,7 @@ function Feedback({ className }: { className?: string }) { '[transition-timing-function:cubic-bezier(.76,0,.23,1)]', !isYes && 'hover:text-brand-600 hover:border-brand-500', isYes && - 'bg-brand text-brand-200 !border-brand disabled:opacity-100 -translate-x-[calc(100%+var(--container-inline-flex-gap,0.5rem))]', + 'bg-brand text-brand-200 border-brand! disabled:opacity-100 -translate-x-[calc(100%+var(--container-inline-flex-gap,0.5rem))]', !showYes && 'opacity-0 invisible' )} onClick={() => handleVote('yes')} @@ -205,8 +205,8 @@ function Feedback({ className }: { className?: string }) { '[transition-delay:200ms,0ms]', '[transition-timing-function:cubic-bezier(.76,0,.23,1)]', 'motion-reduce:[transition-duration:150ms,1ms]', - '!ease-out', - state.type === StateType.Followup && 'opacity-100 visible -translate-x-0' + 'ease-out!', + state.type === StateType.Followup && 'opacity-100 visible translate-x-0' )} > {state.type === StateType.Followup && ( diff --git a/apps/docs/components/Feedback/FeedbackModal.tsx b/apps/docs/components/Feedback/FeedbackModal.tsx index b1e00545bc2..18fc711bf03 100644 --- a/apps/docs/components/Feedback/FeedbackModal.tsx +++ b/apps/docs/components/Feedback/FeedbackModal.tsx @@ -72,7 +72,7 @@ function FeedbackModal({ visible, page, onCancel, onSubmit }: FeedbackModalProps )} />
        - 💡 + 💡
        Need help or support? This feedback form is for documentation improvements only. For technical support, please submit a{' '} diff --git a/apps/docs/components/GuidesSidebar.tsx b/apps/docs/components/GuidesSidebar.tsx index 544efe3fe2f..7031d74dd3d 100644 --- a/apps/docs/components/GuidesSidebar.tsx +++ b/apps/docs/components/GuidesSidebar.tsx @@ -1,17 +1,17 @@ 'use client' +import { Feedback } from '~/components/Feedback' +import { useSendTelemetryEvent } from '~/lib/telemetry' +import { isFeatureEnabled } from 'common' +import { Chatgpt, Claude } from 'icons' 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' -import { Chatgpt } from 'icons' -import { Claude } from 'icons' -import { useSendTelemetryEvent } from '~/lib/telemetry' interface TOCHeader { id?: string diff --git a/apps/docs/components/HomePageCover.tsx b/apps/docs/components/HomePageCover.tsx index c67af16ecfb..706ce54ca8d 100644 --- a/apps/docs/components/HomePageCover.tsx +++ b/apps/docs/components/HomePageCover.tsx @@ -1,13 +1,13 @@ 'use client' -import { ChevronRight, Play, Sparkles } from 'lucide-react' -import Link from 'next/link' -import { useTheme } from 'next-themes' // End of third-party imports - import { isFeatureEnabled, useBreakpoint } from 'common' +import { ChevronRight, Play, Sparkles } from 'lucide-react' +import { useTheme } from 'next-themes' +import Link from 'next/link' import { cn, IconBackground } from 'ui' import { IconPanel } from 'ui-patterns/IconPanel' + import { getCustomContent } from '../lib/custom-content/getCustomContent' import DocsCoverLogo from './DocsCoverLogo' @@ -27,7 +27,7 @@ function AiPrompt({ className }: { className?: string }) { 'transition-colors', className )} - href="/guides/getting-started/ai-prompts" + href="/guides/ai-tools/ai-prompts" > Start with Supabase AI prompts @@ -41,7 +41,7 @@ const HomePageCover = (props) => { const iconSize = isXs ? 'sm' : 'lg' const { homepageHeading } = getCustomContent(['homepage:heading']) const { resolvedTheme } = useTheme() - const isLightMode = resolvedTheme !== 'dark' + const isLightMode = !resolvedTheme?.includes('dark') const frameworks = [ { @@ -161,7 +161,7 @@ const HomePageCover = (props) => { return (
        -
        +
        }> @@ -22,4 +13,4 @@ const JwtGeneratorSimple = () => { ) } -export { JwtGenerator, JwtGeneratorSimple } +export { JwtGeneratorSimple } diff --git a/apps/docs/components/Mermaid.tsx b/apps/docs/components/Mermaid.tsx new file mode 100644 index 00000000000..1ffbf346c4c --- /dev/null +++ b/apps/docs/components/Mermaid.tsx @@ -0,0 +1,25 @@ +import { renderMermaidSVG } from 'beautiful-mermaid' + +interface MermaidProps { + chart: string +} + +export function Mermaid({ chart }: MermaidProps) { + const svg = renderMermaidSVG(chart, { + bg: 'hsl(var(--background-default))', + fg: 'hsl(var(--foreground-default))', + accent: 'hsl(var(--brand-default))', + muted: 'hsl(var(--foreground-light))', + line: 'hsl(var(--border-strong))', + border: 'hsl(var(--border-strong))', + surface: 'hsl(var(--background-surface-200))', + transparent: true, + }) + + return ( +
        + ) +} diff --git a/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx index 773e40b3c33..7faeb2db786 100644 --- a/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/GlobalMobileMenu.tsx @@ -30,7 +30,7 @@ const listItem = { } const itemClassName = - 'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-visible:ring-2 focus-visible:outline-none focus-visible:ring-foreground-lighter focus-visible:rounded' + 'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-visible:ring-2 focus-visible:outline-hidden focus-visible:ring-foreground-lighter focus-visible:rounded-sm' const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => { const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS) @@ -38,7 +38,7 @@ const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => { return ( {section[0].menuItems ? ( @@ -47,7 +47,7 @@ const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => { id={section[0].label} className={cn( 'relative', - activeLabel === section[0].label && '!text-foreground', + activeLabel === section[0].label && 'text-foreground!', itemClassName )} > @@ -75,7 +75,7 @@ const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => { ) : ( {section[0].label} @@ -126,9 +126,9 @@ const GlobalMobileMenu = ({ open, setOpen }: Props) => { initial="hidden" animate="show" exit="exit" - className="bg-overlay fixed overflow-hidden inset-0 z-50 h-screen max-h-screen w-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-[100svh] transform" + className="bg-overlay fixed overflow-hidden inset-0 z-50 h-screen max-h-screen w-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-svh transform" > -
        +
        {
        -
        +
        diff --git a/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx index 29445946b4d..f323b7737a7 100644 --- a/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/GlobalNavigationMenu.tsx @@ -59,7 +59,7 @@ export const useActiveMenuLabel = (menu: typeof GLOBAL_MENU_ITEMS) => { const GlobalNavigationMenu: FC = () => { const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS) const triggerClassName = - 'h-[var(--header-height)] p-2 bg-transparent border-0 border-b-2 border-transparent font-normal rounded-none text-foreground-light hover:text-foreground data-[state=open]:!text-foreground data-[radix-collection-item]:focus-visible:ring-2 data-[radix-collection-item]:focus-visible:ring-foreground-lighter data-[radix-collection-item]:focus-visible:text-foreground h-full focus-visible:rounded !shadow-none outline-none transition-all outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-brand-600' + 'h-(--header-height) p-2 bg-transparent border-0 border-b-2 border-transparent font-normal rounded-none text-foreground-light hover:text-foreground data-open:text-foreground! data-radix-collection-item:focus-visible:ring-2 data-radix-collection-item:focus-visible:ring-foreground-lighter data-radix-collection-item:focus-visible:text-foreground h-full focus-visible:rounded-sm shadow-none! outline-hidden transition-all outline-0 focus-visible:outline-4 focus-visible:outline-offset-1 focus-visible:outline-brand-600' return (
        @@ -68,9 +68,9 @@ const GlobalNavigationMenu: FC = () => { skipDelayDuration={0} className="w-full flex justify-start h-full" renderViewport={false} - viewportClassName="mt-0 max-w-screen overflow-hidden border-0 rounded-none mt-1.5 rounded-md !border-x" + viewportClassName="mt-0 max-w-screen overflow-hidden border-0 rounded-none mt-1.5 rounded-md border-x!" > - + {GLOBAL_MENU_ITEMS.filter((section) => section[0].enabled !== false).map( (section, sectionIdx) => section[0].menuItems ? ( @@ -91,7 +91,7 @@ const GlobalNavigationMenu: FC = () => { section[0].label )} - +
        {section[0].menuItems?.map((menuItem, menuItemIndex) => ( case 'troubleshooting': return + case 'changelog': + return case 'contributing': return case 'deployment': diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index cb85474d1f9..82204bdb456 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -58,6 +58,8 @@ const { 'sdk:swift', ]) +const aiToolsEnabled = true + const jsOnly = !sdkCsharpEnabled && !sdkDartEnabled && !sdkKotlinEnabled && !sdkPythonEnabled && !sdkSwiftEnabled @@ -148,6 +150,13 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [ label: 'Build', menuItems: [ [ + { + label: 'AI Tools', + icon: 'ai-tools', + href: '/guides/ai-tools' as `/${string}`, + level: 'ai_tools', + enabled: aiToolsEnabled, + }, { label: 'Local Development & CLI', icon: 'dev-cli', @@ -327,7 +336,9 @@ export const gettingstarted: NavMenuConstant = { title: 'Start with Supabase', url: '/guides/getting-started', items: [ - { name: 'Features', url: '/guides/getting-started/features' }, + { name: 'Build with AI tools', url: '/guides/ai-tools' }, + { name: 'API Keys', url: '/guides/getting-started/api-keys' }, + { name: 'Local Development', url: '/guides/cli/getting-started' }, { name: 'Architecture', url: '/guides/getting-started/architecture' }, { name: 'Framework Quickstarts', @@ -495,28 +506,6 @@ export const gettingstarted: NavMenuConstant = { }, ], }, - { - name: 'AI Tools', - url: undefined, - items: [ - { - name: 'Prompts', - url: '/guides/getting-started/ai-prompts' as `/${string}`, - }, - { - name: 'Agent Skills', - url: '/guides/getting-started/ai-skills' as `/${string}`, - }, - { - name: 'Supabase MCP server', - url: '/guides/getting-started/mcp' as `/${string}`, - }, - { - name: 'Deploy MCP servers', - url: '/guides/getting-started/byo-mcp' as `/${string}`, - }, - ], - }, ], } @@ -679,6 +668,7 @@ export const PhoneLoginsItems = [ export const auth: NavMenuConstant = { icon: 'auth', title: 'Auth', + url: '/guides/auth', items: [ { name: 'Overview', @@ -1492,9 +1482,7 @@ export const api: NavMenuConstant = { name: 'Security', url: '/guides/api', items: [ - { name: 'How API Keys work', url: '/guides/api/api-keys' }, { name: 'Securing your API', url: '/guides/api/securing-your-api' }, - { name: 'Data API', url: '/guides/database/data-api' }, { name: 'Custom Claims & RBAC', url: '/guides/api/custom-claims-and-role-based-access-control-rbac', @@ -2303,6 +2291,47 @@ export const ai: NavMenuConstant = { ], } +export const ai_tools: NavMenuConstant = { + icon: 'ai-tools', + title: 'AI Tools', + url: '/guides/ai-tools', + enabled: aiToolsEnabled, + items: [ + { name: 'Overview', url: '/guides/ai-tools' }, + { + name: 'Agent Plugin', + url: '/guides/ai-tools/plugins' as `/${string}`, + }, + { + name: 'MCP server', + url: '/guides/ai-tools/mcp' as `/${string}`, + }, + { + name: 'Agent Skills', + url: '/guides/ai-tools/ai-skills' as `/${string}`, + }, + { + name: 'Prompts', + url: '/guides/ai-tools/ai-prompts' as `/${string}`, + }, + + { + name: 'Build AI features', + url: undefined, + items: [ + { + name: 'Deploy MCP servers', + url: '/guides/ai-tools/byo-mcp' as `/${string}`, + }, + { + name: 'Vectors / Embeddings ', + url: '/guides/ai' as `/${string}`, + }, + ], + }, + ], +} + export const local_development: NavMenuConstant = { icon: 'dev-cli', title: 'Local Dev / CLI', @@ -2485,7 +2514,7 @@ export const security: NavMenuConstant = { url: '/guides/deployment/shared-responsibility-model' as `/${string}`, }, { name: 'Row Level Security', url: '/guides/database/postgres/row-level-security' }, - { name: 'Data API', url: '/guides/database/data-api' }, + { name: 'Securing your API', url: '/guides/api/securing-your-api' }, ], }, ], @@ -3072,6 +3101,10 @@ export const integrations: NavMenuConstant = { name: 'Supabase for Platforms', url: '/guides/integrations/supabase-for-platforms', }, + { + name: 'Supabase partner integration guide', + url: '/guides/integrations/partner-integration-guide', + }, ], }, { name: 'Integrations', url: undefined, items: [] }, diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx index 7e9056aad82..5b51b704c83 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.tsx @@ -24,6 +24,7 @@ enum MenuId { Security = 'security', SelfHosting = 'self_hosting', Integrations = 'integrations', + AiTools = 'ai_tools', LocalDevelopment = 'local_development', Contributing = 'contributing', RefJavaScriptV1 = 'reference_javascript_v1', @@ -134,6 +135,10 @@ const menus: Menu[] = [ id: MenuId.LocalDevelopment, type: 'guide', }, + { + id: MenuId.AiTools, + type: 'guide', + }, { id: MenuId.Contributing, type: 'guide', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts index 4f50c7ee18c..556d87971a0 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.utils.ts @@ -110,7 +110,7 @@ export const getMenuId = (pathname: string | null) => { pathname = (pathname ??= '').replace(/^\/guides\//, '') switch (true) { - case pathname.startsWith('ai'): + case pathname.startsWith('ai') && !pathname.startsWith('ai-tools'): return MenuId.Ai case pathname.startsWith('api'): return MenuId.Api @@ -132,6 +132,8 @@ export const getMenuId = (pathname: string | null) => { return MenuId.Integrations case pathname.startsWith('local-development'): return MenuId.LocalDevelopment + case pathname.startsWith('ai-tools'): + return MenuId.AiTools case pathname.startsWith('telemetry'): return MenuId.Telemetry case pathname.startsWith('platform'): diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx index 99eb6d28ec9..c84ee4c287b 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx @@ -112,7 +112,7 @@ const NavigationMenuCliList = ({ currentLevel, setLevel, id }) => {

        diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx index 97620f6186e..cd83cbc4151 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideList.tsx @@ -1,13 +1,13 @@ 'use client' -import * as Accordion from '@radix-ui/react-accordion' +import { usePathname } from 'next/navigation' +import { Accordion } from 'radix-ui' +import { PropsWithChildren } from 'react' import { type NavMenuSection } from '../Navigation.types' +import { MenuId } from './NavigationMenu' import * as NavItems from './NavigationMenu.constants' import NavigationMenuGuideListItems from './NavigationMenuGuideListItems' -import { usePathname } from 'next/navigation' -import { PropsWithChildren } from 'react' -import { MenuId } from './NavigationMenu' const NavigationMenuGuideList = ({ id, diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx index 9f9e2a69158..31192c49a51 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuGuideListItems.tsx @@ -1,9 +1,9 @@ -import * as Accordion from '@radix-ui/react-accordion' -import { usePathname } from 'next/navigation' -import { useTheme } from 'next-themes' import { ChevronDown } from 'lucide-react' +import { useTheme } from 'next-themes' import Image from 'next/legacy/image' import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { Accordion } from 'radix-ui' import React, { useEffect, useRef } from 'react' import MenuIconPicker from './MenuIconPicker' diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index 328e91d112a..18e8d40f6cb 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -1,14 +1,15 @@ 'use client' -import * as Accordion from '@radix-ui/react-accordion' -import { ChevronUp } from 'lucide-react' -import Image from 'next/legacy/image' -import { Fragment, memo } from 'react' -import { cn } from 'ui' -import RevVersionDropdown from '~/components/RefVersionDropdown' import type { ICommonItem, ICommonSection } from '~/components/reference/Reference.types' +import RevVersionDropdown from '~/components/RefVersionDropdown' import { menuState, useMenuActiveRefId } from '~/hooks/useMenuState' import { BASE_PATH } from '~/lib/constants' +import { ChevronUp } from 'lucide-react' +import Image from 'next/legacy/image' +import { Accordion } from 'radix-ui' +import { Fragment, memo } from 'react' +import { cn } from 'ui' + import MenuIconPicker from './MenuIconPicker' import * as NavItems from './NavigationMenu.constants' import { deepFilterSections } from './NavigationMenu.utils' @@ -77,7 +78,7 @@ const FunctionLink = memo(function FunctionLink({ {active && !isSubItem && ( )} {isParent && ( diff --git a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx index c88b9525ed5..ed7537aab81 100644 --- a/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/TopNavBar.tsx @@ -1,16 +1,17 @@ +// End of third-party imports + +import { useIsLoggedIn, useIsUserLoading, useUser } from 'common' +import { isFeatureEnabled } from 'common/enabled-features' +import { DevToolbarTrigger } from 'dev-tools' import { Command, Menu, Search } from 'lucide-react' import dynamic from 'next/dynamic' import Image from 'next/image' import Link from 'next/link' import type { FC } from 'react' import { memo, useState } from 'react' -// End of third-party imports - -import { useIsLoggedIn, useIsUserLoading, useUser } from 'common' -import { isFeatureEnabled } from 'common/enabled-features' -import { DevToolbarTrigger } from 'dev-tools' import { Button, buttonVariants, cn } from 'ui' import { AuthenticatedDropdownMenu, CommandMenuTriggerInput } from 'ui-patterns' + import { getCustomContent } from '../../../lib/custom-content/getCustomContent' import GlobalNavigationMenu from './GlobalNavigationMenu' import useDropdownMenu from './useDropdownMenu' @@ -31,9 +32,9 @@ const TopNavBar: FC = () => { <>

  • + King's Treasury + People's happiness
    + Empty + Overflowing
    + Modest + Satisfied
    + Full + Ecstatic
    ) => ( = ({ {item.title} {item.new && ( - + NEW )} = ({ {item.title} {item.new && ( - + NEW )} @@ -151,7 +151,7 @@ const NavigationItem: React.FC = ({ )} > - + {item.title} (Internal) diff --git a/apps/learn/components/side-navigation.tsx b/apps/learn/components/side-navigation.tsx index 83c393b68d9..0f4c80dae6a 100644 --- a/apps/learn/components/side-navigation.tsx +++ b/apps/learn/components/side-navigation.tsx @@ -1,17 +1,17 @@ 'use client' -import Link from 'next/link' -import { useIsLoggedIn, useUser, logOut } from 'common' -import { AuthenticatedDropdownMenu, type menuItem } from 'ui-patterns' +import { logOut, useIsLoggedIn, useUser } from 'common' import { LogOut, Settings, UserIcon } from 'lucide-react' +import Link from 'next/link' import { useRouter } from 'next/navigation' +import { AuthenticatedDropdownMenu, type menuItem } from 'ui-patterns' +import { CommandMenu } from './command-menu' +import { ThemeSwitcherDropdown } from './theme-switcher-dropdown' import NavigationItem from '@/components/side-navigation-item' import { courses } from '@/config/docs' import { mergeInternalContentIntoSections } from '@/lib/merge-internal-content' import { SidebarNavItem } from '@/types/nav' -import { CommandMenu } from './command-menu' -import { ThemeSwitcherDropdown } from './theme-switcher-dropdown' interface SideNavigationProps { internalPaths: string[] @@ -59,7 +59,7 @@ function SideNavigation({ internalPaths }: SideNavigationProps) { return (