Merge branch 'master' into hf/deprecate-slack-provider

This commit is contained in:
Stojan Dimitrovski authored and GitHub committed 2025-03-31 11:33:04 +02:00
commit 21354ef31d
2519 files changed
+260731 -172446

No files matched your search

+1 -2
View File
@@ -1,5 +1,6 @@
/packages/shared-data/pricing.ts @roryw10 @kevcodez
/packages/shared-data/plans.ts @roryw10 @kevcodez
/packages/common/telemetry-constants.ts @4L3k51 @supabase/growth-eng
/apps/studio/ @supabase/Dashboard
@@ -17,5 +18,3 @@
/apps/studio/components/interfaces/Organization/BillingSettings/ @supabase/security
/apps/studio/components/interfaces/Organization/Documents/ @supabase/security
/apps/studio/pages/new/index.tsx @supabase/security
/apps/studio/lib/constants/telemetry.ts @4L3k51 @loong @pamelachia
-1
View File
@@ -36,7 +36,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
uses: actions/setup-node@v4
+14 -19
View File
@@ -5,9 +5,6 @@ on:
types:
- labeled
permissions:
contents: write
# Cancel old builds on new commit for same workflow + branch/PR
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
@@ -18,20 +15,25 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' && (github.event.label.name == 'autofix') }}
steps:
- name: Calculate number of commits
run: echo "PR_FETCH_DEPTH=$(( ${{ github.event.pull_request.commits }} + 1 ))" >> "${GITHUB_ENV}"
- uses: actions/checkout@v4
with:
sparse-checkout: apps
ref: ${{ github.head_ref }}
token: ${{ secrets.PAT_AUTOFIX }}
fetch-depth: ${{ env.PR_FETCH_DEPTH }}
sparse-checkout: |
packages
apps
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: pnpm
- name: Install required dependencies
run: pnpm i
@@ -40,15 +42,8 @@ jobs:
run: pnpm run format
- name: Commit changes and push to existing branch
run: |
git config --global user.name 'github-tidy-bot'
git config --global user.email 'github-tidy-bot@supabase.com'
if [[ `git status --porcelain` ]]; then
echo "[bot] Changes detected, committing."
echo "[bot] Running in non-squash mode."
git commit -am "ci: Autofix updates from GitHub workflow"
git push
else
echo "[bot] No changes detected, nothing to commit."
fi
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "ci: Autofix updates from GitHub workflow"
commit_user_name: "kevcodez"
commit_user_email: "k.grueneberg1994@gmail.com"
-1
View File
@@ -29,7 +29,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Setup node
@@ -0,0 +1,64 @@
name: '[Docs] Lint v2 (scheduled)'
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
permissions:
contents: write
pull-requests: write
jobs:
lint-all:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
sparse-checkout: |
supa-mdx-lint.config.toml
supa-mdx-lint
apps/docs/content
- name: cache cargo
id: cache-cargo
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: e5609278486869b545fda23b5732727b6e70a1c1
- name: install linter
if: steps.cache-cargo.outputs.cache-hit != 'true'
run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev e5609278486869b545fda23b5732727b6e70a1c1
- name: run linter
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
supa-mdx-lint apps/docs/content || {
echo "Linter failed, attempting to fix errors..."
git config --global user.name 'github-docs-bot'
git config --global user.email 'github-docs-bot@supabase.com'
BRANCH_NAME="bot/docs-lint-fixes"
EXISTING_BRANCH=$(git ls-remote --heads origin $BRANCH_NAME)
if [ -n "$EXISTING_BRANCH" ]; then
git checkout $BRANCH_NAME
else
git checkout -b $BRANCH_NAME
fi
supa-mdx-lint apps/docs/content --fix || FIX_FAILED=1
git add .
git commit -m '[bot] fix lint errors' || true
git push origin $BRANCH_NAME
if [ -z "$EXISTING_BRANCH" ]; then
gh pr create --title '[bot] fix lint errors' --body 'This PR fixes lint errors in the documentation.' --head $BRANCH_NAME
fi
if [ "${FIX_FAILED:-0}" -eq 1 ]; then
echo "Fix did not correct all errors."
exit 1
fi
}
+35 -41
View File
@@ -1,20 +1,11 @@
name: '[Docs] Lint v2'
on:
pull_request:
paths:
- '.github/workflows/docs-lint-v2.yml'
- 'supa-mdx-lint.config.toml'
- 'apps/docs/content/**'
push:
branches:
- master
workflow_dispatch:
env:
CARGO_NET_GIT_FETCH_WITH_CLI: true
permissions:
contents: write
pull-requests: write
jobs:
@@ -22,15 +13,24 @@ jobs:
name: supa-mdx-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
sparse-checkout: |
supa-mdx-lint.config.toml
supa-mdx-lint
apps/docs/content
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 #v3.0.2
id: filter
with:
filters: |
docs:
- 'apps/docs/content/**'
- 'supa-mdx-lint/**'
- 'supa-mdx-lint.config.toml'
- name: cache cargo
id: cache-cargo
if: steps.filter.outputs.docs == 'true'
uses: actions/cache@v4
with:
path: |
@@ -38,50 +38,44 @@ jobs:
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1
key: e5609278486869b545fda23b5732727b6e70a1c1
- name: install linter
if: steps.cache-cargo.outputs.cache-hit != 'true'
run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev 6435a4cd1eeea7c2bbd343731de7e8a5127cb2d1
if: steps.filter.outputs.docs == 'true' && steps.cache-cargo.outputs.cache-hit != 'true'
run: cargo install --locked --git https://github.com/supabase-community/supa-mdx-lint --rev e5609278486869b545fda23b5732727b6e70a1c1
- name: install reviewdog
if: steps.filter.outputs.docs == 'true'
uses: reviewdog/action-setup@3f401fe1d58fe77e10d665ab713057375e39b887 # v1.3.0
with:
reviewdog_version: v0.20.2
- name: run linter on PR
if: github.event_name == 'pull_request'
- name: run linter (internal)
if: steps.filter.outputs.docs == 'true' && github.event.pull_request.head.repo.full_name == github.repository
env:
BASE_REF: ${{ github.base_ref }}
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -o pipefail
git diff --name-only origin/$BASE_REF HEAD \
| { grep -E "^apps/docs/content/guides/" || test $? = 1; } \
| { grep -E "^apps/docs/content/" || test $? = 1; } \
| xargs -r supa-mdx-lint --format rdf \
| reviewdog -f=rdjsonl -reporter=github-pr-review
- name: run linter on push or workflow dispatch
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
| reviewdog -f=rdjsonl -reporter=github-pr-review -tee
- name: run linter (external)
if: steps.filter.outputs.docs == 'true' && github.event.pull_request.head.repo.full_name != github.repository
env:
BASE_REF: ${{ github.base_ref }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH_NAME: ${{ github.head_ref }}
run: |
supa-mdx-lint apps/docs/content || {
echo "Linter failed, attempting to fix errors..."
git config --global user.name 'github-docs-bot'
git config --global user.email 'github-docs-bot@supabase.com'
BRANCH_NAME="bot/docs-lint-fixes"
EXISTING_BRANCH=$(git ls-remote --heads origin $BRANCH_NAME)
if [ -n "$EXISTING_BRANCH" ]; then
git checkout $BRANCH_NAME
else
git checkout -b $BRANCH_NAME
fi
supa-mdx-lint apps/docs/content --fix || FIX_FAILED=1
git add .
git commit -m '[bot] fix lint errors' || true
git push origin $BRANCH_NAME
if [ -z "$EXISTING_BRANCH" ]; then
gh pr create --title '[bot] fix lint errors' --body 'This PR fixes lint errors in the documentation.' --head $BRANCH_NAME
fi
if [ "${FIX_FAILED:-0}" -eq 1 ]; then
echo "Fix did not correct all errors."
exit 1
fi
set -o pipefail
run_lints() {
git diff --name-only origin/$BASE_REF HEAD \
| { grep -E "^apps/docs/content/" || test $? = 1; } \
| xargs -rx -n 1000000000 supa-mdx-lint --format markdown
}
set +e
LINT_RESULTS=$(run_lints)
LINT_EXIT_CODE=$?
set -e
if [[ $LINT_EXIT_CODE -ne 0 ]]; then
gh pr comment $BRANCH_NAME --body "$LINT_RESULTS"
exit 1
fi
@@ -20,7 +20,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
-1
View File
@@ -25,7 +25,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
-1
View File
@@ -26,7 +26,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
-31
View File
@@ -1,31 +0,0 @@
name: reviewdog
on:
pull_request:
paths:
- '.vale.ini'
- 'vale/**'
- 'apps/docs/content/**'
jobs:
vale:
name: runner / vale
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
.vale.ini
vale
apps/docs/content
- name: Get changed files
id: changed-files
env:
BASE_REF: ${{ github.base_ref }}
SHA: ${{ github.sha }}
run: |
git fetch origin $BASE_REF
echo "CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT origin/$BASE_REF $SHA | grep '^apps/docs/content/' | tr '\n' ',' | sed 's/,$//')" >> $GITHUB_OUTPUT
- uses: errata-ai/vale-action@reviewdog
with:
files: '${{ steps.changed-files.outputs.CHANGED_FILES }}'
separator: ','
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup the Supabase CLI
uses: supabase/setup-cli@v1
+50
View File
@@ -0,0 +1,50 @@
name: PG Meta Tests
on:
push:
branches: ['master']
paths:
- 'packages/pg-meta/**/*'
pull_request:
branches: ['master']
paths:
- 'packages/pg-meta/**/*'
# Cancel old builds on new commit for same workflow + branch/PR
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
packages/pg-meta
packages/tsconfig
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- name: Install deps
run: pnpm i
- name: Run tests
run: pnpm --filter=@supabase/pg-meta run test
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
directory: packages/pg-meta/coverage
flags: pg-meta
-3
View File
@@ -21,7 +21,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Setup node
uses: actions/setup-node@v4
@@ -47,7 +46,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Setup node
uses: actions/setup-node@v4
@@ -73,7 +71,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Setup node
uses: actions/setup-node@v4
-1
View File
@@ -41,7 +41,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Setup node
@@ -1,16 +1,18 @@
name: Local Studio Tests
name: Studio E2E Tests
on:
push:
branches: [master]
paths:
- 'packages/pg-meta/**/*'
- 'apps/studio/**'
- 'tests/local-studio-tests/**'
- 'tests/studio-tests/**'
- 'pnpm-lock.yaml'
pull_request:
branches: [master]
paths:
- 'packages/pg-meta/**/*'
- 'apps/studio/**'
- 'tests/local-studio-tests/**'
- 'tests/studio-tests/**'
- 'pnpm-lock.yaml'
# Cancel old builds on new commit for same workflow + branch/PR
@@ -28,7 +30,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
uses: actions/setup-node@v4
@@ -36,17 +37,19 @@ jobs:
node-version-file: '.nvmrc'
cache: 'pnpm'
- uses: supabase/setup-cli@v1
with:
version: latest
- name: Install dependencies
run: pnpm i
- name: Install Playwright Browsers
run: pnpm -C tests/local-studio-tests exec playwright install --with-deps
run: pnpm -C tests/studio-tests exec playwright install --with-deps
- name: Run Playwright tests
run: pnpm test:local-studio
run: pnpm test:e2e:studio-local
# mark the action as succeeded even if the tests failed. This is temporarily until we make the tests more stable.
# continue-on-error: true
- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: tests/local-studio-tests/playwright-report/
path: tests/studio-tests/playwright-report/
retention-days: 7
@@ -34,7 +34,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
uses: actions/setup-node@v4
-1
View File
@@ -22,7 +22,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
-1
View File
@@ -24,7 +24,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
-1
View File
@@ -24,7 +24,6 @@ jobs:
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: 9.10.0
run_install: false
- name: Use Node.js
+3 -1
View File
@@ -1 +1,3 @@
engine-strict=true
engine-strict=true
update-notifier=false
@jsr:registry=https://npm.jsr.io
+6 -1
View File
@@ -9,7 +9,7 @@ apps/**/out
apps/www/schema.sql
apps/www/public/images/*
apps/docs/**/generated/*
apps/docs/examples
apps/docs/examples/*
examples/slack-clone/nextjs-slack-clone/full-schema.sql
# ignore files with custom js formatting
apps/studio/public
@@ -18,3 +18,8 @@ apps/docs/CONTRIBUTING.md
apps/design-system/__registry__
packages/icons/__registry__
packages/icons/src/icons/*.ts
apps/ui-library/__registry__
apps/ui-library/public/r
apps/**/.contentlayer
# invalid JSON file
packages/ui/src/components/Form/examples/PhoneProvidersSchema.json
+4 -4
View File
@@ -12,7 +12,7 @@
- [Shared components](#shared-components)
- [Installing packages](#installing-packages)
- [Running Docker for Supabase Studio](#running-docker-for-supabase-studio)
- [Prerequsites](#prerequsites)
- [Prerequisites](#prerequisites)
- [Get Started](#get-started)
- [Create a pull request](#create-a-pull-request)
- [Issue assignment](#issue-assignment)
@@ -36,8 +36,8 @@ To ensure a positive and inclusive environment, please read our [code of conduct
You will need to install and configure the following dependencies on your machine to build [Supabase](https://supabase.com):
- [Git](http://git-scm.com/)
- [Node.js v20.x (LTS)](http://nodejs.org)
- [Git](https://git-scm.com/)
- [Node.js v20.x (LTS)](https://nodejs.org)
- [pnpm](https://pnpm.io/) version 9.x.x or higher
- [make](https://www.gnu.org/software/make/) or the equivalent to `build-essentials` for your OS
- [Docker](https://docs.docker.com/get-docker/) (to run studio locally)
@@ -129,7 +129,7 @@ For example:
To run Studio locally, you'll need to setup Docker in addition to your NextJS frontend.
#### Prerequsites
#### Prerequisites
First, make sure you have the Docker installed on your device. You can download and install it from [here](https://docs.docker.com/get-docker/).
+4 -4
View File
@@ -52,10 +52,10 @@ You can also [self-host](https://supabase.com/docs/guides/hosting/overview) and
- [Postgres](https://www.postgresql.org/) is an object-relational database system with over 30 years of active development that has earned it a strong reputation for reliability, feature robustness, and performance.
- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients.
- [PostgREST](http://postgrest.org/) is a web server that turns your PostgreSQL database directly into a RESTful API
- [GoTrue](https://github.com/supabase/gotrue) is a JWT based API for managing users and issuing JWT tokens.
- [Storage](https://github.com/supabase/storage-api) provides a RESTful interface for managing Files stored in S3, using Postgres to manage permissions.
- [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API
- [PostgREST](http://postgrest.org/) is a web server that turns your PostgreSQL database directly into a RESTful API.
- [GoTrue](https://github.com/supabase/gotrue) is a JWT-based authentication API that simplifies user sign-ups, logins, and session management in your applications.
- [Storage](https://github.com/supabase/storage-api) a RESTful API for managing files in S3, with Postgres handling permissions.
- [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API.
- [postgres-meta](https://github.com/supabase/postgres-meta) is a RESTful API for managing your Postgres, allowing you to fetch tables, add roles, and run queries, etc.
- [Kong](https://github.com/Kong/kong) is a cloud-native API gateway.
+33
View File
@@ -1864,6 +1864,28 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"toc-demo": {
name: "toc-demo",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/toc-demo")),
source: "",
files: ["registry/default/example/toc-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"toc-single-demo": {
name: "toc-single-demo",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/toc-single-demo")),
source: "",
files: ["registry/default/example/toc-single-demo.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"multi-select-demo": {
name: "multi-select-demo",
type: "components:example",
@@ -1919,6 +1941,17 @@ export const Index: Record<string, any> = {
subcategory: "undefined",
chunks: []
},
"multi-select-combobox-creatable": {
name: "multi-select-combobox-creatable",
type: "components:example",
registryDependencies: undefined,
component: React.lazy(() => import("@/registry/default/example/multi-select-combobox-creatable")),
source: "",
files: ["registry/default/example/multi-select-combobox-creatable.tsx"],
category: "undefined",
subcategory: "undefined",
chunks: []
},
"multi-select-deletable-badge": {
name: "multi-select-deletable-badge",
type: "components:example",
+2 -3
View File
@@ -1,17 +1,16 @@
'use client'
import * as React from 'react'
import { Provider as JotaiProvider } from 'jotai'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { ThemeProviderProps } from 'next-themes/dist/types'
import { TooltipProvider_Shadcn_ } from 'ui'
import { TooltipProvider } from 'ui'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return (
<JotaiProvider>
<NextThemesProvider {...props}>
<TooltipProvider_Shadcn_ delayDuration={0}>{children}</TooltipProvider_Shadcn_>
<TooltipProvider delayDuration={0}>{children}</TooltipProvider>
</NextThemesProvider>
</JotaiProvider>
)
@@ -29,9 +29,9 @@ const SourcePanel = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement> &
y2="208"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="16"
></line>
<line
x1="192"
@@ -40,9 +40,9 @@ const SourcePanel = forwardRef<HTMLDivElement, React.HTMLProps<HTMLDivElement> &
y2="192"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="16"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="16"
></line>
</svg>
<span className="hidden font-bold sm:inline-block">shadcn/ui</span>
+10
View File
@@ -122,6 +122,11 @@ export const docsConfig: DocsConfig = {
href: '/docs/fragments/logs-bar-chart',
items: [],
},
{
title: 'Table of Contents (TOC)',
href: '/docs/fragments/toc',
items: [],
},
],
},
{
@@ -349,6 +354,11 @@ export const docsConfig: DocsConfig = {
href: '/docs/components/slider',
items: [],
},
{
title: 'Sidebar',
href: '/docs/components/sidebar',
items: [],
},
{
title: 'Sonner',
href: '/docs/components/sonner',
File diff suppressed because it is too large. Load diff
@@ -47,6 +47,13 @@ Use `MultiSelectorInput`to add a search input.
<ComponentPreview name="multi-select-combobox" />
### Badge Combo Box with creatable selector
creatable: `boolean`
`creatable` prop on the `MultiSelectorList` component can be used to add a new option if no items match
<ComponentPreview name="multi-select-combobox-creatable" />
### Badge Limit
badgeLimit: `number` | `"wrap"`.
@@ -0,0 +1,18 @@
---
title: Table of Contents (TOC)
description: List of page anchors for the current page.
component: true
fragment: true
---
## Usage
Highlight one or more elements of the TOC if in view.
<ComponentPreview name="toc-demo" description="" peekCode wide />
## Single=true
Highlight one element of the TOC at a time.
<ComponentPreview name="toc-single-demo" description="" peekCode wide />
+3 -2
View File
@@ -6,8 +6,9 @@
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev --port 3003",
"build": "pnpm run content:build && pnpm run build:registry && next build",
"build": "pnpm run content:build && pnpm run build:registry && pnpm run build:llms && next build",
"build:registry": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-registry.mts && prettier --log-level silent --write \"registry/**/*.{ts,tsx,mdx}\" --cache",
"build:llms": "tsx --tsconfig ./tsconfig.scripts.json ./scripts/build-llms-txt.ts",
"start": "next start",
"lint": "next lint",
"content:build": "contentlayer2 build",
@@ -59,7 +60,7 @@
"shiki": "^1.1.7",
"tailwindcss": "^3.3.0",
"tsconfig": "workspace:*",
"tsx": "^4.7.0",
"tsx": "^4.19.3",
"typescript": "~5.5.0",
"unist-builder": "3.0.0"
}
+154
View File
@@ -0,0 +1,154 @@
# Supabase Design System
Last updated: 2025-03-05T12:59:50.911Z
## Overview
The Design System used in Supabase Studio.
## Docs
- [Changelog](https://supabase-design-system.vercel.app/design-system/docs/changelog)
- Latest updates and announcements.
- [Color usage](https://supabase-design-system.vercel.app/design-system/docs/color-usage)
- Colors system breakdown with best practise
- [Accordion](https://supabase-design-system.vercel.app/design-system/docs/components/accordion)
- A vertically stacked set of interactive headings that each reveal a section of content.
- [Alert](https://supabase-design-system.vercel.app/design-system/docs/components/alert copy)
- Displays a callout for user attention.
- [Alert Dialog](https://supabase-design-system.vercel.app/design-system/docs/components/alert-dialog)
- A modal dialog that interrupts the user with important content and expects a response.
- [Alert](https://supabase-design-system.vercel.app/design-system/docs/components/alert)
- Displays a callout for user attention.
- [Aspect Ratio](https://supabase-design-system.vercel.app/design-system/docs/components/aspect-ratio)
- Displays content within a desired ratio.
- [Atom components](https://supabase-design-system.vercel.app/design-system/docs/components/atom-components)
- [Avatar](https://supabase-design-system.vercel.app/design-system/docs/components/avatar)
- An image element with a fallback for representing the user.
- [Badge](https://supabase-design-system.vercel.app/design-system/docs/components/badge)
- Displays a badge or a component that looks like a badge.
- [Breadcrumb](https://supabase-design-system.vercel.app/design-system/docs/components/breadcrumb)
- Displays the path to the current resource using a hierarchy of links.
- [Button](https://supabase-design-system.vercel.app/design-system/docs/components/button)
- Displays a button or a component that looks like a button.
- [Calendar](https://supabase-design-system.vercel.app/design-system/docs/components/calendar)
- A date field component that allows users to enter and edit date.
- [Card](https://supabase-design-system.vercel.app/design-system/docs/components/card)
- Displays a card with header, content, and footer.
- [Carousel](https://supabase-design-system.vercel.app/design-system/docs/components/carousel)
- A carousel with motion and swipe built using Embla.
- [Chart](https://supabase-design-system.vercel.app/design-system/docs/components/chart)
- Beautiful charts. Built using Recharts. Copy and paste into your apps.
- [Checkbox](https://supabase-design-system.vercel.app/design-system/docs/components/checkbox)
- A control that allows the user to toggle between checked and not checked.
- [Collapsible](https://supabase-design-system.vercel.app/design-system/docs/components/collapsible)
- An interactive component which expands/collapses a panel.
- [Combobox](https://supabase-design-system.vercel.app/design-system/docs/components/combobox)
- Autocomplete input and command palette with a list of suggestions.
- [Command](https://supabase-design-system.vercel.app/design-system/docs/components/command)
- Fast, composable, unstyled command menu for React.
- [Command Menu (cmdk)](https://supabase-design-system.vercel.app/design-system/docs/components/commandmenu)
- A central command menu that acts as a control center with searchable actions.
- [Context Menu](https://supabase-design-system.vercel.app/design-system/docs/components/context-menu)
- Displays a menu to the user — such as a set of actions or functions — triggered by a button.
- [Data Table](https://supabase-design-system.vercel.app/design-system/docs/components/data-table)
- Powerful table and datagrids built using TanStack Table.
- [Date Picker](https://supabase-design-system.vercel.app/design-system/docs/components/date-picker)
- A date picker component with range and presets.
- [Dialog](https://supabase-design-system.vercel.app/design-system/docs/components/dialog)
- A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
- [Drawer](https://supabase-design-system.vercel.app/design-system/docs/components/drawer)
- A drawer component for React.
- [Dropdown Menu](https://supabase-design-system.vercel.app/design-system/docs/components/dropdown-menu)
- Displays a menu to the user — such as a set of actions or functions — triggered by a button.
- [Expanding Textarea](https://supabase-design-system.vercel.app/design-system/docs/components/expanding-textarea)
- Displays a textarea which autoexpands (or shrinks) based on its content.
- [React Hook Form](https://supabase-design-system.vercel.app/design-system/docs/components/form)
- Building forms with React Hook Form and Zod.
- [Fragment components](https://supabase-design-system.vercel.app/design-system/docs/components/fragment-components)
- ''
- [Hover Card](https://supabase-design-system.vercel.app/design-system/docs/components/hover-card)
- For sighted users to preview content available behind a link.
- [Input OTP](https://supabase-design-system.vercel.app/design-system/docs/components/input-otp)
- Accessible one-time password component with copy paste functionality.
- [Input](https://supabase-design-system.vercel.app/design-system/docs/components/input)
- Displays a form input field or a component that looks like an input field.
- [Label](https://supabase-design-system.vercel.app/design-system/docs/components/label)
- Renders an accessible label associated with controls.
- [Menubar](https://supabase-design-system.vercel.app/design-system/docs/components/menubar)
- A visually persistent menu common in desktop applications that provides quick access to a consistent set of commands.
- [Navigation Menu](https://supabase-design-system.vercel.app/design-system/docs/components/navigation-menu)
- A collection of links for navigating websites.
- [Pagination](https://supabase-design-system.vercel.app/design-system/docs/components/pagination)
- Pagination with page navigation, next and previous links.
- [Popover](https://supabase-design-system.vercel.app/design-system/docs/components/popover)
- Displays rich content in a portal, triggered by a button.
- [Progress](https://supabase-design-system.vercel.app/design-system/docs/components/progress)
- Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.
- [Radio Group Card](https://supabase-design-system.vercel.app/design-system/docs/components/radio-group-card)
- A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
- [Radio Group Stacked](https://supabase-design-system.vercel.app/design-system/docs/components/radio-group-stacked)
- A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
- [Radio Group](https://supabase-design-system.vercel.app/design-system/docs/components/radio-group)
- A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
- [Resizable](https://supabase-design-system.vercel.app/design-system/docs/components/resizable)
- Accessible resizable panel groups and layouts with keyboard support.
- [Scroll-area](https://supabase-design-system.vercel.app/design-system/docs/components/scroll-area)
- Augments native scroll functionality for custom, cross-browser styling.
- [Select](https://supabase-design-system.vercel.app/design-system/docs/components/select)
- Displays a list of options for the user to pick from—triggered by a button.
- [Separator](https://supabase-design-system.vercel.app/design-system/docs/components/separator)
- Visually or semantically separates content.
- [Sheet](https://supabase-design-system.vercel.app/design-system/docs/components/sheet)
- Extends the Dialog component to display content that complements the main content of the screen.
- [Sidebar](https://supabase-design-system.vercel.app/design-system/docs/components/sidebar)
- A composable, themeable and customizable sidebar component.
- [Skeleton](https://supabase-design-system.vercel.app/design-system/docs/components/skeleton)
- Use to show a placeholder while content is loading.
- [Slider](https://supabase-design-system.vercel.app/design-system/docs/components/slider)
- An input where the user selects a value from within a given range.
- [Sonner](https://supabase-design-system.vercel.app/design-system/docs/components/sonner)
- An opinionated toast component for React.
- [Switch](https://supabase-design-system.vercel.app/design-system/docs/components/switch)
- A control that allows the user to toggle between checked and not checked.
- [Table](https://supabase-design-system.vercel.app/design-system/docs/components/table)
- A responsive table component.
- [Tabs](https://supabase-design-system.vercel.app/design-system/docs/components/tabs)
- A set of layered sections of content—known as tab panels—that are displayed one at a time.
- [Textarea](https://supabase-design-system.vercel.app/design-system/docs/components/textarea)
- Displays a form textarea or a component that looks like a textarea.
- [Toggle Group](https://supabase-design-system.vercel.app/design-system/docs/components/toggle-group)
- A set of two-state buttons that can be toggled on or off.
- [Toggle](https://supabase-design-system.vercel.app/design-system/docs/components/toggle)
- A two-state button that can be either on or off.
- [Tooltip](https://supabase-design-system.vercel.app/design-system/docs/components/tooltip)
- A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.
- [Tree View](https://supabase-design-system.vercel.app/design-system/docs/components/tree-view)
- A tree view that assembles all the functionalities of the Accordion component to create a tree view.
- [Typography](https://supabase-design-system.vercel.app/design-system/docs/components/typography)
- Styles for headings, paragraphs, lists...etc
- [Figma](https://supabase-design-system.vercel.app/design-system/docs/figma)
- Every component recreated in Figma. With customizable props, typography and icons.
- [Admonition](https://supabase-design-system.vercel.app/design-system/docs/fragments/admonition)
- Displays a callout for user attention.
- [Assistant Chat](https://supabase-design-system.vercel.app/design-system/docs/fragments/assistant-chat)
- Bespoke Assistant chat text area, with support for a commands Popover.
- [Form Item Layout](https://supabase-design-system.vercel.app/design-system/docs/fragments/form-item-layout)
- A helper component that provides a layout for form items.
- [Info Tooltip](https://supabase-design-system.vercel.app/design-system/docs/fragments/info-tooltip)
- InfoTooltip provides a tooltip with an information icon.
- [Inner Side Menu](https://supabase-design-system.vercel.app/design-system/docs/fragments/inner-side-menu)
- InnerSideMenu is a component that provides a collapsible side menu with multiple sections.
- [Logs Bar Chart](https://supabase-design-system.vercel.app/design-system/docs/fragments/logs-bar-chart)
- A stacked bar chart that displays logs errors and successes.
- [Modal](https://supabase-design-system.vercel.app/design-system/docs/fragments/modal)
- A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
- [Multi Select](https://supabase-design-system.vercel.app/design-system/docs/fragments/multi-select)
- Multiple selection component.
- [Text Confirm Dialog](https://supabase-design-system.vercel.app/design-system/docs/fragments/text-confirm-dialog)
- A modal dialog that interrupts the user with important content and expects a response.
- [Icons](https://supabase-design-system.vercel.app/design-system/docs/icons)
- Icons system breakdown. Copy values of Icons.
- [Introduction](https://supabase-design-system.vercel.app/design-system/docs/index)
- Components and patterns that you can copy and paste into Supabase apps. Accessible. Customizable. Open Source.
- [Tailwind Classes](https://supabase-design-system.vercel.app/design-system/docs/tailwind-classes)
- Icons system breakdown. Copy values of Icons.
- [Themes](https://supabase-design-system.vercel.app/design-system/docs/theming)
- Themes used in Supabase
@@ -3,7 +3,7 @@
import * as React from 'react'
import { Calendar, MoreHorizontal, Tags, Trash, User } from 'lucide-react'
import { Button, CommandList_Shadcn_ } from 'ui'
import { Button, CommandList_Shadcn_, ScrollArea } from 'ui'
import {
Command_Shadcn_,
CommandEmpty_Shadcn_,
@@ -75,23 +75,25 @@ export default function ComboboxDropdownMenu() {
<DropdownMenuSubContent className="p-0">
<Command_Shadcn_>
<CommandInput_Shadcn_ placeholder="Filter label..." autoFocus={true} />
<CommandList_Shadcn_>
<CommandEmpty_Shadcn_>No label found.</CommandEmpty_Shadcn_>
<CommandGroup_Shadcn_>
{labels.map((label) => (
<CommandItem_Shadcn_
key={label}
value={label}
onSelect={(value) => {
setLabel(value)
setOpen(false)
}}
>
{label}
</CommandItem_Shadcn_>
))}
</CommandGroup_Shadcn_>
</CommandList_Shadcn_>
<ScrollArea className="h-20">
<CommandList_Shadcn_>
<CommandEmpty_Shadcn_>No label found.</CommandEmpty_Shadcn_>
<CommandGroup_Shadcn_>
{labels.map((label) => (
<CommandItem_Shadcn_
key={label}
value={label}
onSelect={(value) => {
setLabel(value)
setOpen(false)
}}
>
{label}
</CommandItem_Shadcn_>
))}
</CommandGroup_Shadcn_>
</CommandList_Shadcn_>
</ScrollArea>
</Command_Shadcn_>
</DropdownMenuSubContent>
</DropdownMenuSub>
@@ -9,6 +9,7 @@ export default function LogsBarChartDemo() {
timestamp: date.toISOString(),
ok_count: Math.floor(Math.random() * 100), // Random value 0-99
error_count: Math.floor(Math.random() * 50), // Random value 0-50
warning_count: Math.floor(Math.random() * 50), // Random value 0-50
}
}).reverse()
@@ -0,0 +1,34 @@
import { useState } from 'react'
import {
MultiSelector,
MultiSelectorContent,
MultiSelectorInput,
MultiSelectorItem,
MultiSelectorList,
MultiSelectorTrigger,
} from 'ui-patterns/multi-select'
export default function MultiSelectDemo() {
const [selectedValues, setSelectedValues] = useState<string[]>([])
return (
<MultiSelector values={selectedValues} onValuesChange={setSelectedValues}>
<MultiSelectorTrigger className="w-72" label="Select fruits" badgeLimit="wrap" />
<MultiSelectorContent>
<MultiSelectorInput placeholder="Search fruits" showResetIcon />
<MultiSelectorList creatable>
<MultiSelectorItem value="Mango">Mango</MultiSelectorItem>
<MultiSelectorItem value="Date">Date</MultiSelectorItem>
<MultiSelectorItem value="Apple">Apple</MultiSelectorItem>
<MultiSelectorItem value="Elderberrie">Elderberrie</MultiSelectorItem>
<MultiSelectorItem value="Fig">Fig</MultiSelectorItem>
<MultiSelectorItem value="Grape">Grape</MultiSelectorItem>
<MultiSelectorItem value="Banana">Banana</MultiSelectorItem>
<MultiSelectorItem value="Kiwi">Kiwi</MultiSelectorItem>
<MultiSelectorItem value="Strawberry">Strawberry</MultiSelectorItem>
<MultiSelectorItem value="Cherry">Cherry</MultiSelectorItem>
</MultiSelectorList>
</MultiSelectorContent>
</MultiSelector>
)
}
@@ -0,0 +1,227 @@
'use client'
import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'
import { Toc, TOCItems, TOCScrollArea } from 'ui-patterns'
export default function MultiSelectDemo() {
return (
<TocAnchorsProvider>
<div
id="example-toc-demo"
className="p-4 md:p-8 grid md:grid-cols-4 gap-4 max-h-[600px] overflow-y-auto"
>
<div className="md:col-span-3">
<div className="prose max-w-none">
<h1>Getting Started with Cloud Computing</h1>
<h2 id="introduction">
Introduction
<a
href="#introduction"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Cloud computing has revolutionized how we build and deploy applications. This guide
will walk you through the fundamental concepts and best practices.
</p>
<h2 id="key-concepts">
Key Concepts
<a
href="#key-concepts"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Before diving deep into cloud services, it's important to understand the basic
building blocks that make cloud computing possible.
</p>
<h3 id="iaas">
Infrastructure as a Service (IaaS)
<a
href="#iaas"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
IaaS provides virtualized computing resources over the internet. This includes virtual
machines, storage, and networking.
</p>
<h3 id="paas">
Platform as a Service (PaaS)
<a
href="#paas"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
PaaS delivers a platform allowing customers to develop, run, and manage applications
without dealing with infrastructure.
</p>
<h2 id="best-practices">
Best Practices
<a
href="#best-practices"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Following cloud computing best practices ensures optimal performance, security, and
cost-effectiveness.
</p>
<h3 id="security">
Security Considerations
<a
href="#security"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
Security should be your top priority when working with cloud services. Implement
proper authentication, encryption, and access controls.
</p>
<h3 id="cost-optimization">
Cost Optimization
<a
href="#cost-optimization"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
Learn how to optimize your cloud spending through resource planning, monitoring, and
implementing cost-saving strategies.
</p>
<h2 id="conclusion">
Conclusion
<a
href="#conclusion"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Cloud computing continues to evolve, offering new possibilities for businesses and
developers alike. Stay updated with the latest trends and best practices.
</p>
</div>
</div>
<TocComponent />
</div>
</TocAnchorsProvider>
)
}
const TocComponent = () => {
const { toc } = useTocAnchors()
return (
<Toc className="sticky top-0 border-l">
<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 className="-ml-[2px]">
<TOCItems items={toc} />
</TOCScrollArea>
</Toc>
)
}
import { type AnchorProviderProps, AnchorProvider } from 'ui-patterns'
interface TOCHeader {
id?: string
text: string
link: string
level: number
}
const TocAnchorsContext = createContext<AnchorProviderProps | undefined>(undefined)
const useTocAnchors = () => {
const context = useContext(TocAnchorsContext)
if (!context) {
throw new Error('useTocAnchors must be used within an TocAnchorsContext')
}
return context
}
const TocAnchorsProvider = ({ children }: PropsWithChildren) => {
const [tocList, setTocList] = useState<TOCHeader[]>([])
const toc = tocList.map((item) => ({
title: item.text,
url: item.link,
depth: item.level,
}))
useEffect(() => {
/**
* Because we're directly querying the DOM, needs the setTimeout so the DOM
* update will happen first.
*/
const timeoutHandle = setTimeout(() => {
const headings = Array.from(
document.querySelector('#example-toc-demo')?.querySelectorAll('h2, h3') ?? []
)
const newHeadings = headings
.filter((heading) => heading.id)
.map((heading) => {
const text = heading?.textContent?.replace('#', '')
const link = heading.querySelector('a')?.getAttribute('href')
if (!link) return null
const level = heading.tagName === 'H2' ? 2 : 3
return { text, link, level } as Partial<TOCHeader>
})
.filter((x): x is TOCHeader => !!x && !!x.text && !!x.link && !!x.level)
setTocList(newHeadings)
}, 100)
return () => clearTimeout(timeoutHandle)
/**
* window.location.href needed to recalculate toc when page changes,
* `useSubscribeTocRerender` above will trigger the rerender
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [typeof window !== 'undefined' && window.location.href])
return (
<TocAnchorsContext.Provider value={{ toc }}>
<AnchorProvider toc={toc} single={false}>
{children}
</AnchorProvider>
</TocAnchorsContext.Provider>
)
}
@@ -0,0 +1,227 @@
'use client'
import { createContext, PropsWithChildren, useContext, useEffect, useState } from 'react'
import { Toc, TOCItems, TOCScrollArea } from 'ui-patterns'
export default function MultiSelectDemo() {
return (
<TocAnchorsProvider>
<div
id="example-toc-single-demo"
className="p-4 md:p-8 grid md:grid-cols-4 gap-4 max-h-[600px] overflow-y-auto"
>
<div className="md:col-span-3">
<div className="prose max-w-none">
<h1>Getting Started with Cloud Computing</h1>
<h2 id="single-introduction">
Introduction
<a
href="#single-introduction"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Cloud computing has revolutionized how we build and deploy applications. This guide
will walk you through the fundamental concepts and best practices.
</p>
<h2 id="single-key-concepts">
Key Concepts
<a
href="#single-key-concepts"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Before diving deep into cloud services, it's important to understand the basic
building blocks that make cloud computing possible.
</p>
<h3 id="single-iaas">
Infrastructure as a Service (IaaS)
<a
href="#single-iaas"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
IaaS provides virtualized computing resources over the internet. This includes virtual
machines, storage, and networking.
</p>
<h3 id="single-paas">
Platform as a Service (PaaS)
<a
href="#single-paas"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
PaaS delivers a platform allowing customers to develop, run, and manage applications
without dealing with infrastructure.
</p>
<h2 id="single-best-practices">
Best Practices
<a
href="#single-best-practices"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Following cloud computing best practices ensures optimal performance, security, and
cost-effectiveness.
</p>
<h3 id="single-security">
Security Considerations
<a
href="#single-security"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
Security should be your top priority when working with cloud services. Implement
proper authentication, encryption, and access controls.
</p>
<h3 id="single-cost-optimization">
Cost Optimization
<a
href="#single-cost-optimization"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h3>
<p>
Learn how to optimize your cloud spending through resource planning, monitoring, and
implementing cost-saving strategies.
</p>
<h2 id="single-conclusion">
Conclusion
<a
href="#single-conclusion"
aria-hidden="true"
className="ml-2 opacity-0 group-hover:opacity-100 transition"
>
<span aria-hidden="true">#</span>
</a>
</h2>
<p>
Cloud computing continues to evolve, offering new possibilities for businesses and
developers alike. Stay updated with the latest trends and best practices.
</p>
</div>
</div>
<TocComponent />
</div>
</TocAnchorsProvider>
)
}
const TocComponent = () => {
const { toc } = useTocAnchors()
return (
<Toc className="sticky top-0 border-l">
<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 className="-ml-[2px]">
<TOCItems items={toc} />
</TOCScrollArea>
</Toc>
)
}
import { type AnchorProviderProps, AnchorProvider } from 'ui-patterns'
interface TOCHeader {
id?: string
text: string
link: string
level: number
}
const TocAnchorsContext = createContext<AnchorProviderProps | undefined>(undefined)
const useTocAnchors = () => {
const context = useContext(TocAnchorsContext)
if (!context) {
throw new Error('useTocAnchors must be used within an TocAnchorsContext')
}
return context
}
const TocAnchorsProvider = ({ children }: PropsWithChildren) => {
const [tocList, setTocList] = useState<TOCHeader[]>([])
const toc = tocList.map((item) => ({
title: item.text,
url: item.link,
depth: item.level,
}))
useEffect(() => {
/**
* Because we're directly querying the DOM, needs the setTimeout so the DOM
* update will happen first.
*/
const timeoutHandle = setTimeout(() => {
const headings = Array.from(
document.querySelector('#example-toc-single-demo')?.querySelectorAll('h2, h3') ?? []
)
const newHeadings = headings
.filter((heading) => heading.id)
.map((heading) => {
const text = heading?.textContent?.replace('#', '')
const link = heading.querySelector('a')?.getAttribute('href')
if (!link) return null
const level = heading.tagName === 'H2' ? 2 : 3
return { text, link, level } as Partial<TOCHeader>
})
.filter((x): x is TOCHeader => !!x && !!x.text && !!x.link && !!x.level)
setTocList(newHeadings)
}, 100)
return () => clearTimeout(timeoutHandle)
/**
* window.location.href needed to recalculate toc when page changes,
* `useSubscribeTocRerender` above will trigger the rerender
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [typeof window !== 'undefined' && window.location.href])
return (
<TocAnchorsContext.Provider value={{ toc }}>
<AnchorProvider toc={toc} single={true}>
{children}
</AnchorProvider>
</TocAnchorsContext.Provider>
)
}
@@ -1,22 +1,16 @@
import { Button } from 'ui'
import {
Tooltip_Shadcn_,
TooltipContent_Shadcn_,
TooltipProvider_Shadcn_,
TooltipTrigger_Shadcn_,
} from 'ui'
import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui'
export default function TooltipDemo() {
return (
<TooltipProvider_Shadcn_>
<Tooltip_Shadcn_>
<TooltipTrigger_Shadcn_ asChild>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button type="outline">Hover</Button>
</TooltipTrigger_Shadcn_>
<TooltipContent_Shadcn_>
</TooltipTrigger>
<TooltipContent>
<p>Add to library</p>
</TooltipContent_Shadcn_>
</Tooltip_Shadcn_>
</TooltipProvider_Shadcn_>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}
+15
View File
@@ -1066,6 +1066,16 @@ export const examples: Registry = [
type: 'components:example',
files: ['example/inner-side-menu-with-search.tsx'],
},
{
name: 'toc-demo',
type: 'components:example',
files: ['example/toc-demo.tsx'],
},
{
name: 'toc-single-demo',
type: 'components:example',
files: ['example/toc-single-demo.tsx'],
},
{
name: 'multi-select-demo',
type: 'components:example',
@@ -1091,6 +1101,11 @@ export const examples: Registry = [
type: 'components:example',
files: ['example/multi-select-combobox.tsx'],
},
{
name: 'multi-select-combobox-creatable',
type: 'components:example',
files: ['example/multi-select-combobox-creatable.tsx'],
},
{
name: 'multi-select-deletable-badge',
type: 'components:example',
@@ -0,0 +1,106 @@
import { registry } from '@/registry/registry'
import fs from 'fs'
import path from 'path'
const BASE_URL = 'https://supabase-design-system.vercel.app/design-system/docs'
interface DocMeta {
title: string
description?: string
path: string
}
console.log('🤖 Building llms.txt')
// Function to extract frontmatter from MDX files
function extractFrontmatter(content: string): { title?: string; description?: string } {
const frontmatterRegex = /---\n([\s\S]*?)\n---/
const match = content.match(frontmatterRegex)
if (!match) return {}
const frontmatter = match[1]
const titleMatch = frontmatter.match(/title:\s*(.*)/)
const descriptionMatch = frontmatter.match(/description:\s*(.*)/)
return {
title: titleMatch?.[1],
description: descriptionMatch?.[1],
}
}
// Function to recursively get all MDX files
function getMdxFiles(dir: string): string[] {
const files: string[] = []
const entries = fs.readdirSync(dir, { withFileTypes: true })
for (const entry of entries) {
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
files.push(...getMdxFiles(fullPath))
} else if (entry.name.endsWith('.mdx')) {
files.push(fullPath)
}
}
return files
}
// Function to get all MDX files and their metadata
function getDocFiles(): DocMeta[] {
const docsDir = path.join(process.cwd(), 'content/docs')
const mdxFiles = getMdxFiles(docsDir)
const docs: DocMeta[] = []
for (const fullPath of mdxFiles) {
const content = fs.readFileSync(fullPath, 'utf-8')
const { title, description } = extractFrontmatter(content)
if (title) {
// Get relative path and convert to URL path
const relativePath = path.relative(docsDir, fullPath)
const urlPath = relativePath
.replace(/\.mdx$/, '')
.replace(/\/index$/, '')
.replace(/\\/g, '/')
docs.push({
title,
description,
path: urlPath,
})
}
}
return docs
}
// Generate the llms.txt content
const docs = getDocFiles()
let content = `# Supabase Design System
Last updated: ${new Date().toISOString()}
## Overview
The Design System used in Supabase Studio.
## Docs
`
// Add documentation links
for (const doc of docs) {
const url = `${BASE_URL}/${doc.path}`
content += `- [${doc.title}](${url})`
if (doc.description) {
content += `\n - ${doc.description}`
}
content += '\n'
}
// Write the file
const publicDir = path.join(process.cwd(), 'public')
if (!fs.existsSync(publicDir)) {
fs.mkdirSync(publicDir, { recursive: true })
}
fs.writeFileSync(path.join(publicDir, 'llms.txt'), content)
console.log('✅ Generated llms.txt in public directory')
+2 -1
View File
@@ -8,7 +8,8 @@ module.exports = config({
// purge styles from grid library
//
'./../../packages/ui/src/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/!(node_modules)/**/*.{tsx,ts,js}',
'./../../packages/ui-patterns/**/*.{tsx,ts,js}',
'!./../../packages/ui-patterns/node_modules/**/*', // ignore node_modules rule
],
theme: {
extend: {
+3
View File
@@ -26,6 +26,9 @@ yarn-error.log*
# Sitemap
public/sitemap.xml
# llms.txt (generated)
public/llms.txt
public/llms/
# Copied examples folder
/examples/
+61 -21
View File
@@ -5,7 +5,8 @@ import type { HTMLAttributes } from 'react'
import { useEffect, useState } from 'react'
import { useBreakpoint } from 'common'
import { cn, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
import { cn, Separator, Sheet, SheetContent, SheetHeader, SheetTrigger } from 'ui'
import { Feedback } from '~/components/Feedback'
interface TocItem extends HTMLAttributes<HTMLElement> {
label: string
@@ -18,7 +19,7 @@ export function ContributingToc({ className }: { className?: string }) {
useEffect(() => {
const headings = [
...document.querySelectorAll('article.prose > h2,h3'),
...document.querySelectorAll('article.prose > h2,h3:not(#feedback-title)'),
] as Array<HTMLHeadingElement>
const tocItems = headings
.filter((heading) => !!heading.id)
@@ -30,20 +31,16 @@ export function ContributingToc({ className }: { className?: string }) {
}, [])
return mobileToc ? (
<MobileToc
items={tocItems}
className={cn(
'[--local-top-spacing:2rem]',
'fixed top-[calc(var(--header-height)+var(--local-top-spacing))] right-8'
)}
/>
<MobileToc items={tocItems} />
) : (
<TocBase
items={tocItems}
className={cn(
'[--local-top-spacing:5rem]',
'fixed top-[calc(var(--header-height)+var(--local-top-spacing))] right-8 w-36',
'xl:border-l border-foreground-muted'
'border-l thin-scrollbar overflow-y-auto px-2 hidden lg:block',
'col-span-3 self-start sticky',
'top-[calc(var(--header-height)+1px+2rem)] max-h-[calc(100vh-var(--header-height)-3rem)]',
className
)}
/>
)
@@ -52,31 +49,74 @@ export function ContributingToc({ className }: { className?: string }) {
function MobileToc({ items, className }: { items: Array<TocItem>; className?: string }) {
const [open, setOpen] = useState(false)
useEffect(() => {
const onHashChanged = () => setOpen(false)
window.addEventListener('hashchange', onHashChanged)
return () => {
window.removeEventListener('hashchange', onHashChanged)
}
}, [])
return (
<Popover_Shadcn_ open={open} onOpenChange={setOpen}>
<PopoverTrigger_Shadcn_ className={cn('border rounded p-2', className)}>
<Menu />
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger
className={cn(
'fixed z-0 inset-0 top-auto w-full rounded-t-lg border border-b-0 p-4 bg-studio flex items-center gap-2 text-foreground-light text-sm',
className
)}
>
<Menu size={16} strokeWidth={1.5} className="text-foreground-light" />
<span className="sr-only">
{open ? 'Close table of contents' : 'Open table of contents'}
</span>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ align="end" className="w-48">
<TocBase items={items} />
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
<span>On this page</span>
</SheetTrigger>
<SheetContent
side="bottom"
size="lg"
className={cn(
'w-full flex flex-col gap-0 p-0 rounded-t-lg overflow-hidden',
!open && 'top-[calc(100vh-100px)]'
)}
>
<SheetHeader className="py-0 px-4">
<SheetTrigger
className={cn(
'w-full py-4 flex items-center gap-2 text-foreground-light text-sm',
className
)}
>
<Menu size={16} strokeWidth={1.5} className="text-foreground-light" />
<span className="sr-only">
{open ? 'Close table of contents' : 'Open table of contents'}
</span>
<span>On this page</span>
</SheetTrigger>
</SheetHeader>
<div className="w-full flex-1 p-4 pb-8 overflow-y-auto thin-scrollbar">
<TocBase items={items} />
</div>
</SheetContent>
</Sheet>
)
}
function TocBase({ items, className }: { items: Array<TocItem>; className?: string }) {
return (
<nav aria-label="Table of contents" className={cn('px-4 text-foreground-light', className)}>
<ul className="flex flex-col gap-1">
<nav aria-label="Table of contents" className={cn('text-foreground-lighter', className)}>
<span className="hidden lg:block font-mono text-xs uppercase text-foreground px-5 mb-6">
On this page
</span>
<ul className="toc-menu list-none lg:pl-5 text-[0.8rem] grid gap-2">
{items.map((item) => (
<li key={item.anchor} className="overflow-hidden truncate">
<a href={`#${item.anchor}`}>{item.label}</a>
</li>
))}
</ul>
<Separator className="lg:w-[calc(100%-2rem)] lg:ml-5 my-4 lg:my-8" />
<Feedback className="pl-0 lg:pl-5" />
</nav>
)
}
+111 -12
View File
@@ -171,15 +171,16 @@ Or, you can use the `<$CodeSample />` component to include code samples from a s
If the file is within the `supabase/supabase` repo's `examples` directory:
{/* prettier-ignore */}
```mdx
<$CodeSample
path="/relative/path/from/examples/directory.js"
{/_ Array of [start, end] line numbers to include.
{/* Array of [start, end] line numbers to include.
Line numbers are 1-indexed and inclusive.
-1 indicates the final line. _/}
-1 indicates the final line. */}
lines={[[1, 3], [5, -1]]}
{/* Optional, displays as a file name on the code block */}
meta="display/path.js"
meta="name=display/path.js"
/>
```
@@ -193,12 +194,92 @@ repo="cli"
commit="1623aa9b95ec90e21c5bae5a0d50dcf272abe92f"
path="/relative/path/from/root.js"
lines={[[1, 3], [5, -1]]}
meta="display/path.js"
meta="name=display/path.js"
/>
```
The repo must be public, the org must be on the allow list, and the commit must be an immutable SHA (not a mutable tag or branch name).
#### Multi-file code samples
Multi-file code samples use the `<$CodeTabs>` annotation:
````mdx
<$CodeTabs>
```js name=a.js
console.log('Hello, world!')
```
```js name=b.js
console.log('Goodbye, world!')
```
</$CodeTabs>
````
This produces:
<$CodeTabs>
```js name=a.js
console.log('Hello, world!')
```
```js name=b.js
console.log('Goodbye, world!')
```
</$CodeTabs>
If you want to give a file name for your code sample, you can also use `<$CodeTabs>` with only one code block.
#### TypeScript type hints
JavaScript and TypeScript code blocks include type hints:
````mdx
```js
let hello = 'Hello, world!'
```
````
Hover over `hello` to see the hint.
```js
let hello = 'Hello, world!'
```
The entire code block must be a valid compilation target for TypeScript. That is, if you have undefined variables or missing imports, type hints won't be shown. You can add extra statements above the cut for variable definitions, etc.
You can also import the `supabase-js` library here:
````mdx
```js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('dummy', 'client')
// ---cut---
const result = supabase.auth.signInWithPassword({
email: 'test@example.com',
password: 'test1234',
})
```
````
Note the hidden statements above the cut. Hover over `signInWithPassword` to see the hint:
```js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('dummy', 'client')
// ---cut---
const result = supabase.auth.signInWithPassword({
email: 'test@example.com',
password: 'test1234',
})
```
### Icons
The following icons are available. They can be styled with [Tailwind](https://tailwindcss.com/) classes:
@@ -388,6 +469,14 @@ void main() async {
</TabPanel>
</Tabs>
### Info Tooltip
The <InfoTooltip tooltipContent="This is an info tooltip">InfoTooltip</InfoTooltip> component is used to add more context to a word or phrase via tooltip.
```mdx
<InfoTooltip tooltipContent="The open source Firebase alternative.">Supabase</InfoTooltip>
```
## Partials
We incorporate content reuse in the docs to avoid duplication. If you find yourself writing the same content over and over, you can put it in a partial instead. Here are some examples of commonly used partials:
@@ -407,10 +496,10 @@ We incorporate content reuse in the docs to avoid duplication. If you find yours
>
```mdx
<DatabaseSetup />
<$Partial path="database_setup.mdx" />
```
<DatabaseSetup />
<$Partial path="database_setup.mdx" />
</AccordionItem>
@@ -422,10 +511,10 @@ We incorporate content reuse in the docs to avoid duplication. If you find yours
>
```mdx
<CreateClientSnippet />
<$Partial path="create_client_snippet.mdx" />
```
<CreateClientSnippet />
<$Partial path="create_client_snippet.mdx" />
</AccordionItem>
@@ -434,8 +523,18 @@ We incorporate content reuse in the docs to avoid duplication. If you find yours
To make a new partial:
1. Make a new MDX file in `apps/docs/components/MDX`.
1. Make a new MDX file in `apps/docs/content/_partials`.
1. Write your reusable content.
1. Inside `apps/docs/components/MDX/partials.tsx`, import and re-export your partial.
1. Inside `apps/docs/features/docs/mdx.shared.tsx`, import your partial and include it in the `components` object.
1. You can now use your partial inside any other MDX file by using: `<YourPartialName />`.
1. You can now use your partial inside any other MDX file by using: `<$Partial path="path/from/_partials/directory.mdx" />`.
If you need compile-time variable replacement, you can define variables that get replaced with strings.
```mdx
<$Partial path="path/to/file.mdx" variables={{ "substitute": "this" }}>
```
```mdx
Here is the partial text to {{ .substitute }}.
```
Only string replacements are possible at this time. For more complex use cases, consider making a custom component.
+13 -8
View File
@@ -3,24 +3,29 @@ import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { ContributingToc } from '~/app/contributing/ContributingToC'
import { MDXProviderGuides } from '~/features/docs/GuidesMdx.client'
import { MDXRemoteBase } from '~/features/docs/MdxBase'
import { LayoutMainContent } from '~/layouts/DefaultLayout'
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
import Breadcrumbs from '~/components/Breadcrumbs'
export default async function ContributingPage() {
const contentFile = join(dirname(fileURLToPath(import.meta.url)), 'content.mdx')
const content = await readFile(contentFile, 'utf-8')
return (
<SidebarSkeleton hideSideNav>
<div className="px-8 py-16">
<article className="prose mx-auto">
<MDXProviderGuides>
<SidebarSkeleton>
<LayoutMainContent className="pb-0 grid grid-cols-12 relative gap-4">
<div className="col-span-12 lg:col-span-9">
<Breadcrumbs className="mb-2 col-span-full" />
<article
id="contributing"
className="prose max-w-none relative transition-all ease-out duration-100"
>
<MDXRemoteBase source={content} />
</MDXProviderGuides>
</article>
</article>
</div>
<ContributingToc />
</div>
</LayoutMainContent>
</SidebarSkeleton>
)
}
@@ -1,21 +0,0 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug: string[] }
const GuidePage = async ({ params }: { params: Params }) => {
const data = await getGuidesMarkdown(params)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams()
const generateMetadata = genGuideMeta(getGuidesMarkdown)
export default GuidePage
export { generateStaticParams, generateMetadata }
@@ -1,146 +0,0 @@
import matter from 'gray-matter'
import { type SerializeOptions } from 'next-mdx-remote/dist/types'
import { notFound } from 'next/navigation'
import rehypeSlug from 'rehype-slug'
import { genGuideMeta, removeRedundantH1 } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
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'
// Each external docs page is mapped to a local page
const pageMap = [
{
remoteFile: 'README.md',
meta: {
title: 'Terraform Provider',
},
useRoot: true,
},
{
slug: 'tutorial',
remoteFile: 'tutorial.md',
meta: {
title: 'Using the Supabase Terraform Provider',
},
},
]
interface Params {
slug?: string[]
}
const TerraformDocs = async ({ params }: { params: Params }) => {
const { meta, ...data } = await getContent(params)
const options = {
mdxOptions: {
remarkPlugins: [remarkMkDocsAdmonition, remarkPyMdownTabs, [removeTitle, meta.title]],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
} as SerializeOptions
return <GuideTemplate mdxOptions={options} meta={meta} {...data} />
}
/**
* The GitHub repo uses relative links, which don't lead to the right locations
* in docs.
*
* @param url The original link, as written in the Markdown file
* @returns The rewritten link
*/
const urlTransform: UrlTransformFunction = (url: string) => {
try {
const placeholderHostname = 'placeholder'
const { hostname, pathname, hash } = new URL(url, `http://${placeholderHostname}`)
// Don't modify a url with a FQDN or a url that's only a hash
if (hostname !== placeholderHostname || pathname === '/') {
return url
}
const getBasename = (pathname: string) =>
pathname.endsWith('.md') ? pathname.replace(/\.md$/, '') : pathname
const stripLeadingPrefix = (pathname: string) => pathname.replace(/^\//, '')
const stripLeadingDocs = (pathname: string) => pathname.replace(/^docs\//, '')
const relativePath = stripLeadingPrefix(getBasename(pathname))
const page = pageMap.find(
({ remoteFile, useRoot }) =>
(useRoot && `${relativePath}.md` === remoteFile) ||
(!useRoot && `${stripLeadingDocs(relativePath)}.md` === remoteFile)
)
if (page) {
return 'terraform' + `/${page.slug}` + hash
}
// If we don't have this page in our docs, link to GitHub repo
return `https://github.com/${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}${pathname}${hash}`
} catch (err) {
console.error('Error transforming markdown URL', err)
return url
}
}
/**
* Fetch markdown from external repo
*/
const getContent = async ({ slug }: Params) => {
const [requestedSlug] = slug ?? []
const page = pageMap.find((page) => page.slug === requestedSlug)
if (!page) {
notFound()
}
const { meta, remoteFile, useRoot } = page
const editLink = newEditLink(
`${terraformDocsOrg}/${terraformDocsRepo}/blob/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)
let response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${useRoot ? '' : `${terraformDocsDocsDir}/`}${remoteFile}`
)
let rawContent = await response.text()
// Strip out HTML comments
rawContent = rawContent.replace(/<!--.*?-->/, '')
let { content, data } = matter(rawContent)
// Remove the title from the content so it isn't duplicated in the final display
content = removeRedundantH1(content)
Object.assign(meta, data)
if (!isValidGuideFrontmatter(meta)) {
throw Error('Guide frontmatter is invalid.')
}
return {
pathname:
`/guides/platform/terraform${slug?.length ? `/${slug.join('/')}` : ''}` satisfies `/${string}`,
meta,
content,
editLink,
}
}
const generateStaticParams = async () => pageMap.map(({ slug }) => ({ slug: slug ? [slug] : [] }))
const generateMetadata = genGuideMeta(getContent)
export default TerraformDocs
export { generateStaticParams, generateMetadata }
@@ -1,61 +0,0 @@
import { cache, type PropsWithChildren } from 'react'
import { IS_PLATFORM } from 'common'
import { NavMenuSection } from '~/components/Navigation/Navigation.types'
import Layout from '~/layouts/guides'
import { supabaseMisc } from '~/lib/supabaseMisc'
import { getAiPrompts } from './getting-started/ai-prompts/[slug]/AiPrompts.utils'
// Revalidate occasionally to pick up changes to partners
// 60 seconds/minute * 60 minutes/hour * 24 hours/day
// export const revalidate = 86_400
const GuidesLayout = async ({ children }: PropsWithChildren) => {
const [partnerNavItems, promptNavItems] = await Promise.all([getPartners(), getPrompts()])
const additionalNavItems =
partnerNavItems.length > 0 || promptNavItems.length > 0
? { integrations: partnerNavItems, prompts: promptNavItems }
: undefined
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
}
async function getPrompts() {
const prompts = await getAiPrompts()
return prompts.map(
(prompt) =>
({
name: prompt.heading,
url: `/guides/getting-started/ai-prompts/${prompt.filename}`,
}) as Partial<NavMenuSection>
)
}
const getPartners = cache(getPartnersImpl)
async function getPartnersImpl() {
if (!IS_PLATFORM) return []
const { data, error } = await supabaseMisc()
.from('partners')
.select('slug, title')
.eq('approved', true)
.eq('type', 'technology')
.order('title')
if (error) {
console.error(new Error('Error fetching partners', { cause: error }))
}
const partnerNavItems = (data ?? []).map(
(partner) =>
({
name: partner.title,
url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
}) as Partial<NavMenuSection>
)
return partnerNavItems
}
export default GuidesLayout
@@ -1,106 +0,0 @@
import ReactMarkdown from 'react-markdown'
import { CodeBlock } from 'ui'
import { Heading } from 'ui/src/components/CustomHTMLElements'
import { type TOCHeader } from '~/components/GuidesTableOfContents'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import type { Parameter } from '~/lib/refGenerator/refTypes'
import specFile from '~/spec/cli_v1_config.yaml' with { type: 'yml' }
const meta = {
title: 'Supabase CLI config',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/cli/config',
meta,
}))
const tocList: TOCHeader[] = []
const content = specFile.info.tags.map((tag: { id: string; title: string }, id: number) => {
tocList.push({ id: `${id}`, text: tag.title, link: `${tag.id}-config`, level: 2 })
return (
<div>
<Heading tag="h2">{tag.title} Config</Heading>
{specFile.parameters
.filter((param: Parameter) => param.tags && param.tags[0] === tag.id)
.map((parameter: Parameter, id: number) => {
tocList.push({ id: `${id}`, text: parameter.id, link: `#${parameter.id}`, level: 3 })
return <Info parameter={parameter} />
})}
</div>
)
})
const Config = () => {
const editLink = newEditLink('supabase/supabase/blob/master/apps/docs/spec/cli_v1_config.yaml')
return (
<GuideTemplate meta={meta} editLink={editLink}>
<ReactMarkdown>{specFile.info.description}</ReactMarkdown>
<div>{content}</div>
</GuideTemplate>
)
}
function Info({ parameter }: { parameter: Parameter }) {
return (
<div className="mt-8">
<div>
<Heading tag="h3" parseAnchors={false} customAnchor={parameter.id}>
<code>{parameter.title}</code>
</Heading>
<div className="border-b pb-8" key={parameter.id}>
<div className=" mb-16">
<div>
<table className="table-auto">
<thead>
<tr>
<th className="text-left">Name</th>
<th className="text-left">Default</th>
<th className="text-left">Required</th>
</tr>
</thead>
<tbody>
<tr>
<td>{parameter.id}</td>
<td>{parameter.default ? parameter.default.toString() : 'None'}</td>
<td>{parameter.required !== undefined && parameter.required.toString()}</td>
</tr>
</tbody>
</table>
</div>
<div className="mb-4 scroll-mt-16 mt-0 ">
<p className="text-sm font-bold uppercase">Description</p>
<ReactMarkdown>{parameter.description}</ReactMarkdown>
</div>
{parameter.usage && (
<div className="mb-4 scroll-mt-16 mt-0 ">
<p className="text-sm font-bold uppercase">Usage</p>
<CodeBlock className="useless-code-block-class" language="py">
{parameter.usage}
</CodeBlock>
</div>
)}
{parameter.links && (
<div>
<p className="text-sm font-bold uppercase">See also</p>
<ul>
{parameter.links.map((link) => (
<li key={link.link}>
<a href={link.link}>{link.name}</a>
</li>
))}
</ul>
</div>
)}
</div>
</div>
</div>
</div>
)
}
export default Config
export { generateMetadata }
@@ -1,410 +0,0 @@
import { codeBlock } from 'common-tags'
import { Check, PlusCircle } from 'lucide-react'
import Link from 'next/link'
import ReactMarkdown from 'react-markdown'
import {
CodeBlock,
Heading,
PopoverContent_Shadcn_,
PopoverTrigger_Shadcn_,
Popover_Shadcn_,
} from 'ui'
import { genGuideMeta } from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template'
import { fetchRevalidatePerDay } from '~/features/helpers.fetch'
import { TabPanel, Tabs } from '~/features/ui/Tabs'
import {
terraformDocsBranch,
terraformDocsDocsDir,
terraformDocsOrg,
terraformDocsRepo,
} from '../terraformConstants'
const meta = {
title: 'Terraform Provider reference',
subtitle: 'Resources and data sources available through the Terraform Provider',
}
const generateMetadata = genGuideMeta(() => ({
pathname: '/guides/platform/terraform/reference',
meta,
}))
function ProviderSettings({ schema }: { schema: any }) {
const attributes = schema.block.attributes
const example = codeBlock`
provider "supabase" {
${Object.keys(attributes).map(
(attribute) =>
`${attribute} = ${attributes[attribute].type === 'string' ? `""` : '<value>'}`
)}
}
`
return (
<section aria-labelledby="provider-settings" className="prose max-w-none">
<Heading tag="h2">Provider settings</Heading>
<p>
Use these settings to configure your Supabase provider and authenticate to your Supabase
project.
</p>
<Heading tag="h3">Example usage</Heading>
<CodeBlock className="not-prose">{example}</CodeBlock>
<Heading tag="h3">Details</Heading>
{/* extra div because width restriction doesn't work on table itself */}
<div className="w-full overflow-auto">
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Optional</th>
<th>Sensitive</th>
</tr>
</thead>
<tbody>
{Object.keys(schema.block.attributes).map((attribute) => (
<tr key={attribute}>
<td>{attribute}</td>
<td>
<ReactMarkdown>{attributes[attribute].description}</ReactMarkdown>
</td>
<td>{attributes[attribute].type}</td>
<td className="align-middle">
{attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{attributes[attribute].sensitive && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
)
}
function Resources({ schema }: { schema: any }) {
return (
<section aria-labelledby="resources" className="prose max-w-none">
<Heading tag="h2">Resources</Heading>
<p>You can configure these resources using the Supabase Terraform provider:</p>
<Tabs>
{Object.keys(schema).map((resource) => (
<TabPanel id={resource} label={resource}>
<Heading tag="h4">Example usage</Heading>
<CodeBlock className="not-prose">{codeBlock`
resource "${resource}" "<label>" {
${Object.keys(schema[resource].block.attributes)
.filter((attribute) => !schema[resource].block.attributes[attribute].computed)
.map(
(attribute) =>
`${attribute} = ${
schema[resource].block.attributes[attribute].type === 'string'
? `""`
: '<value>'
}`
)}
}
`}</CodeBlock>
<Heading tag="h4">Details</Heading>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Required</th>
<th>Optional</th>
<th>Read-only</th>
</tr>
</thead>
<tbody>
{Object.keys(schema[resource].block.attributes).map((attribute) => (
<tr key={attribute}>
<td>
<code>{attribute}</code>
</td>
<td>
<ReactMarkdown>
{schema[resource].block.attributes[attribute].description}
</ReactMarkdown>
</td>
<td>
{schema[resource].block.attributes[attribute].type ?? (
<Popover_Shadcn_>
<PopoverTrigger_Shadcn_ asChild>
<button className="flex items-center justify-between gap-2">
Nested type
<PlusCircle size={14} className="shrink-0" />
</button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="max-h-[50vh] overflow-auto">
<ul>
{Object.keys(
schema[resource].block.attributes[attribute].nested_type.attributes
).map((nestedAttribute) => (
<li key={nestedAttribute}>
{nestedAttribute}
<ul>
<li>
<ReactMarkdown className="*:!m-0">
{
schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].description
}
</ReactMarkdown>
</li>
<li>
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].type ?? 'nested type'}
</li>
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].required && <li>Required</li>}
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].optional && <li>Optional</li>}
{schema[resource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].computed && <li>Read-only</li>}
</ul>
</li>
))}
</ul>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].required && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[resource].block.attributes[attribute].computed && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</TabPanel>
))}
</Tabs>
</section>
)
}
function DataSources({ schema }: { schema: any }) {
return (
<section aria-labelledby="data-sources" className="prose max-w-none">
<Heading tag="h2">Data sources</Heading>
<p>You can read these resources using the Supabase Terraform provider:</p>
<Tabs>
{Object.keys(schema).map((dataSource) => (
<TabPanel id={dataSource} label={dataSource}>
<Heading tag="h4">Example usage</Heading>
<CodeBlock className="not-prose">{codeBlock`
resource "${dataSource}" "all" {
${Object.keys(schema[dataSource].block.attributes)
.filter(
(attribute) => !schema[dataSource].block.attributes[attribute].computed
)
.map(
(attribute) =>
`${attribute} = ${
schema[dataSource].block.attributes[attribute].type === 'string'
? `""`
: '<value>'
}`
)}
}
`}</CodeBlock>
<Heading tag="h4">Details</Heading>
<table>
<thead>
<tr>
<th>Attribute</th>
<th>Description</th>
<th>Type</th>
<th>Required</th>
<th>Optional</th>
<th>Read-only</th>
</tr>
</thead>
<tbody>
{Object.keys(schema[dataSource].block.attributes).map((attribute) => (
<tr key={attribute}>
<td>
<code>{attribute}</code>
</td>
<td>
<ReactMarkdown>
{schema[dataSource].block.attributes[attribute].description}
</ReactMarkdown>
</td>
<td>
{schema[dataSource].block.attributes[attribute].type ?? (
<Popover_Shadcn_>
<PopoverTrigger_Shadcn_ asChild>
<button className="flex items-center justify-between gap-2">
Nested type
<PlusCircle size={14} />
</button>
</PopoverTrigger_Shadcn_>
<PopoverContent_Shadcn_ className="max-h-[50vh] overflow-auto">
{schema[dataSource].block.attributes[attribute].nested_type
.nesting_mode === 'set' && 'Array of:'}
<ul>
{Object.keys(
schema[dataSource].block.attributes[attribute].nested_type
.attributes
).map((nestedAttribute) => (
<li key={nestedAttribute}>
{nestedAttribute}
<ul>
<li>
<ReactMarkdown className="*:!m-0">
{
schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].description
}
</ReactMarkdown>
</li>
<li>
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].type ?? 'nested type'}
</li>
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].required && <li>Required</li>}
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].optional && <li>Optional</li>}
{schema[dataSource].block.attributes[attribute].nested_type
.attributes[nestedAttribute].computed && <li>Read-only</li>}
</ul>
</li>
))}
</ul>
</PopoverContent_Shadcn_>
</Popover_Shadcn_>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].required && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].optional && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
<td className="align-middle">
{schema[dataSource].block.attributes[attribute].computed && (
<>
<Check className="ml-[2.5ch]" />
<span className="sr-only">true</span>
</>
)}
</td>
</tr>
))}
</tbody>
</table>
</TabPanel>
))}
</Tabs>
</section>
)
}
const TerraformReferencePage = async () => {
const { schema } = await getSchema()
const editLink = newEditLink('supabase/terraform-provider-supabase')
return (
<GuideTemplate meta={meta} editLink={editLink}>
The Terraform Provider provices access to{' '}
<Link
href="https://developer.hashicorp.com/terraform/language/resources"
rel="noopener noreferrer"
>
resources
</Link>{' '}
and{' '}
<Link
href="https://developer.hashicorp.com/terraform/language/data-sources"
rel="noreferrer noopener"
>
data sources
</Link>
. Resources are infrastructure objects, such as a Supabase project, that you can declaratively
configure. Data sources are sources of information about your Supabase instances.
<ProviderSettings
schema={schema.provider_schemas['registry.terraform.io/supabase/supabase'].provider}
/>
<Resources
schema={schema.provider_schemas['registry.terraform.io/supabase/supabase'].resource_schemas}
/>
<DataSources
schema={
schema.provider_schemas['registry.terraform.io/supabase/supabase'].data_source_schemas
}
/>
</GuideTemplate>
)
}
/**
* Fetch JSON schema from external repo
*/
const getSchema = async () => {
let response = await fetchRevalidatePerDay(
`https://raw.githubusercontent.com/${terraformDocsOrg}/${terraformDocsRepo}/${terraformDocsBranch}/${terraformDocsDocsDir}/schema.json`
)
if (!response.ok) throw Error('Failed to fetch Terraform JSON schema from GitHub')
const schema = await response.json()
return {
schema,
}
}
export default TerraformReferencePage
export { generateMetadata }
@@ -1,9 +0,0 @@
/**
* Information on where to fetch docs content
*/
const terraformDocsOrg = 'supabase'
const terraformDocsRepo = 'terraform-provider-supabase'
const terraformDocsBranch = 'v1.1.3'
const terraformDocsDocsDir = 'docs'
export { terraformDocsOrg, terraformDocsRepo, terraformDocsBranch, terraformDocsDocsDir }
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const AiGuidePage = async ({ params }: { params: Params }) => {
const slug = ['ai', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('ai')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['ai', ...(params.slug ?? [])])
)
export default AiGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function AiLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -10,6 +10,8 @@ import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLin
import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
export const dynamicParams = false
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'vecs'
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const ApiGuidePage = async ({ params }: { params: Params }) => {
const slug = ['api', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('api')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['api', ...(params.slug ?? [])])
)
export default ApiGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function ApiLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const AuthGuidePage = async ({ params }: { params: Params }) => {
const slug = ['auth', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('auth')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['auth', ...(params.slug ?? [])])
)
export default AuthGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function AuthLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const CronGuidePage = async ({ params }: { params: Params }) => {
const slug = ['cron', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('cron')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['cron', ...(params.slug ?? [])])
)
export default CronGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function CronLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const DatabaseGuidePage = async ({ params }: { params: Params }) => {
const slug = ['database', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('database')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['database', ...(params.slug ?? [])])
)
export default DatabaseGuidePage
export { generateStaticParams, generateMetadata }
@@ -18,6 +18,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
export const dynamicParams = false
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'wrappers'
@@ -48,6 +50,13 @@ const pageMap = [
},
remoteFile: 'bigquery.md',
},
{
slug: 'clerk',
meta: {
title: 'Clerk',
},
remoteFile: 'clerk.md',
},
{
slug: 'clickhouse',
meta: {
@@ -83,6 +92,13 @@ const pageMap = [
},
remoteFile: 'mssql.md',
},
{
slug: 'notion',
meta: {
title: 'Notion',
},
remoteFile: 'notion.md',
},
{
slug: 'paddle',
meta: {
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function DatabaseLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const DeploymentGuidePage = async ({ params }: { params: Params }) => {
const slug = ['deployment', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('deployment')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['deployment', ...(params.slug ?? [])])
)
export default DeploymentGuidePage
export { generateStaticParams, generateMetadata }
@@ -11,6 +11,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
export const dynamicParams = false
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'setup-cli'
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function DeploymentLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -18,6 +18,8 @@ import {
terraformDocsRepo,
} from '../terraformConstants'
export const dynamicParams = false
// Each external docs page is mapped to a local page
const pageMap = [
{
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const FunctionsGuidePage = async ({ params }: { params: Params }) => {
const slug = ['functions', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('functions')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['functions', ...(params.slug ?? [])])
)
export default FunctionsGuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function FunctionsLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const GettingStartedGuidePage = async ({ params }: { params: Params }) => {
const slug = ['getting-started', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('getting-started')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['getting-started', ...(params.slug ?? [])])
)
export default GettingStartedGuidePage
export { generateStaticParams, generateMetadata }
@@ -1,3 +1,4 @@
import matter from 'gray-matter'
import type { Code, Heading, Root } from 'mdast'
import { fromMarkdown } from 'mdast-util-from-markdown'
import { toMarkdown } from 'mdast-util-to-markdown'
@@ -11,7 +12,8 @@ import { EXAMPLES_DIRECTORY } from '~/lib/docs'
const PROMPTS_DIRECTORY = join(EXAMPLES_DIRECTORY, 'prompts')
function parseMarkdown(markdown: string) {
const mdast = fromMarkdown(markdown)
const { content: withoutFrontmatter } = matter(markdown)
const mdast = fromMarkdown(withoutFrontmatter)
let heading = ''
visit(mdast, 'heading', (node: Heading) => {
@@ -23,7 +23,7 @@ ${content}
return (
<GuideTemplate
meta={{ title: `API Prompt: ${heading}` }}
meta={{ title: `AI Prompt: ${heading}` }}
content={content}
editLink={newEditLink(`supabase/supabase/blob/master/examples/prompts/${slug}.md`)}
/>
@@ -0,0 +1,20 @@
import { type NavMenuSection } from '~/components/Navigation/Navigation.types'
import Layout from '~/layouts/guides'
import { getAiPrompts } from '../getting-started/ai-prompts/[slug]/AiPrompts.utils'
export default async function GettingStartedLayout({ children }: { children: React.ReactNode }) {
const additionalNavItems = { prompts: await getPrompts() }
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
}
async function getPrompts() {
const prompts = await getAiPrompts()
return prompts.map(
(prompt) =>
({
name: prompt.heading,
url: `/guides/getting-started/ai-prompts/${prompt.filename}`,
}) as Partial<NavMenuSection>
)
}
@@ -11,6 +11,8 @@ import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition'
import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle'
import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs'
export const dynamicParams = false
// We fetch these docs at build time from an external repo
const org = 'supabase'
const repo = 'pg_graphql'
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function GraphQlLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const IntegrationsGuidePage = async ({ params }: { params: Params }) => {
const slug = ['integrations', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('integrations')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['integrations', ...(params.slug ?? [])])
)
export default IntegrationsGuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,39 @@
import { IS_PLATFORM } from 'common'
import { unstable_cache } from 'next/cache'
import { type NavMenuSection } from '~/components/Navigation/Navigation.types'
import Layout from '~/layouts/guides'
import { supabaseMisc } from '~/lib/supabaseMisc'
export default async function IntegrationsLayout({ children }: { children: React.ReactNode }) {
const additionalNavItems = { integrations: await getPartners() }
return <Layout additionalNavItems={additionalNavItems}>{children}</Layout>
}
// Will need to turn on revalidation later, just turning it off for now so we
// can slowly turn things back on while monitoring usage
const getPartners = unstable_cache(getPartnersImpl, [], { revalidate: false })
async function getPartnersImpl() {
if (!IS_PLATFORM) return []
const { data, error } = await supabaseMisc()
.from('partners')
.select('slug, title')
.eq('approved', true)
.eq('type', 'technology')
.order('title')
if (error) {
console.error(new Error('Error fetching partners', { cause: error }))
}
const partnerNavItems = (data ?? []).map(
(partner) =>
({
name: partner.title,
url: `https://supabase.com/partners/integrations/${partner.slug}` as `https://${string}`,
}) as Partial<NavMenuSection>
)
return partnerNavItems
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const LocalDevelopmentGuidePage = async ({ params }: { params: Params }) => {
const slug = ['local-development', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('local-development')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['local-development', ...(params.slug ?? [])])
)
export default LocalDevelopmentGuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function LocalDevelopmentLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const PlatformGuidePage = async ({ params }: { params: Params }) => {
const slug = ['platform', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('platform')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['platform', ...(params.slug ?? [])])
)
export default PlatformGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function PlatformLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const QueuesGuidePage = async ({ params }: { params: Params }) => {
const slug = ['queues', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('queues')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['queues', ...(params.slug ?? [])])
)
export default QueuesGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function QueuesLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const RealtimeGuidePage = async ({ params }: { params: Params }) => {
const slug = ['realtime', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('realtime')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['realtime', ...(params.slug ?? [])])
)
export default RealtimeGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function RealtimeLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const ResourcesGuidePage = async ({ params }: { params: Params }) => {
const slug = ['resources', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('resources')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['resources', ...(params.slug ?? [])])
)
export default ResourcesGuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function ResourcesLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const SecurityGuidePage = async ({ params }: { params: Params }) => {
const slug = ['security', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('security')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['security', ...(params.slug ?? [])])
)
export default SecurityGuidePage
export { generateStaticParams, generateMetadata }
+5
View File
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function SecurityLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
@@ -0,0 +1,25 @@
import {
getGuidesMarkdown,
genGuideMeta,
genGuidesStaticParams,
} from '~/features/docs/GuidesMdx.utils'
import { GuideTemplate } from '~/features/docs/GuidesMdx.template'
export const dynamicParams = false
type Params = { slug?: string[] }
const SelfHostingGuidePage = async ({ params }: { params: Params }) => {
const slug = ['self-hosting', ...(params.slug ?? [])]
const data = await getGuidesMarkdown(slug)
return <GuideTemplate {...data!} />
}
const generateStaticParams = genGuidesStaticParams('self-hosting')
const generateMetadata = genGuideMeta((params: { slug?: string[] }) =>
getGuidesMarkdown(['self-hosting', ...(params.slug ?? [])])
)
export default SelfHostingGuidePage
export { generateStaticParams, generateMetadata }
@@ -0,0 +1,5 @@
import Layout from '~/layouts/guides'
export default async function SelfHostingLayout({ children }: { children: React.ReactNode }) {
return <Layout>{children}</Layout>
}
Loaded 100 of 2519 files, more files were not shown because too many files have changed in this diff. Show more