Merge branch 'master' into docs/postgres-js-pipelining-transaction-mode

This commit is contained in:
Szymon Mentel authored and GitHub committed 2026-09-07 12:53:45 +02:00
commit 0a96e2ba80
22 files changed
+320 -15

No files matched your search

@@ -19,6 +19,7 @@ const ICON_ASSETS: Record<string, { icon: string; hasLightIcon?: boolean }> = {
'gemini-cli': { icon: '/docs/img/icons/agent-gemini-cli-icon', hasLightIcon: false },
'github-copilot': { icon: '/docs/img/icons/agent-copilot-icon', hasLightIcon: true },
kimi: { icon: '/docs/img/icons/agent-kimi-icon', hasLightIcon: true },
omp: { icon: '/docs/img/icons/agent-omp-icon', hasLightIcon: false },
vscode: { icon: '/docs/img/icons/agent-vscode-icon', hasLightIcon: false },
antigravity: { icon: '/docs/img/icons/agent-antigravity-icon', hasLightIcon: false },
windsurf: { icon: '/docs/img/icons/agent-devin-icon', hasLightIcon: true },
@@ -46,6 +47,7 @@ const TAGLINES: Record<string, string> = {
goose: 'Your native open source AI agent — desktop app, CLI, and API.',
kimi: 'Engineered to drop into any dev workflow and get programming tasks done fast.',
kiro: 'Move beyond AI coding to agentic engineering.',
omp: 'A coding agent with the IDE wired in.',
opencode: 'The open source AI coding agent.',
vscode: 'The open source AI code editor — your home for multi-agent development.',
// Pre-acquisition tagline (Cognition/Devin acquired Windsurf in 2025) — see "Open questions".
@@ -63,6 +63,14 @@ export const PLUGIN_CLIENTS: PluginClient[] = [
repoUrl: 'https://github.com/supabase-community/supabase-plugin',
docsUrl: 'https://www.kimi.com/code/docs/en/kimi-code-cli/customization/plugins.html',
},
{
key: 'omp',
label: 'omp',
icon: 'omp',
repoUrl: 'https://github.com/supabase-community/supabase-plugin',
docsUrl: 'https://github.com/can1357/oh-my-pi/blob/main/docs/marketplace.md',
docsLinkText: 'View omp plugin docs',
},
{
key: 'vscode',
label: 'VS Code',
@@ -169,6 +169,38 @@ function PluginInstructions({ client }: { client: PluginClient }) {
</div>
)
}
if (client.key === 'omp') {
return (
<div className="space-y-3">
<p className="text-sm text-foreground-light">
omp reads the Claude Code plugin format, so install the Supabase plugin from the{' '}
<a
href="https://github.com/anthropics/claude-plugins-official"
target="_blank"
rel="noopener noreferrer"
className="text-brand-link hover:underline"
>
official Anthropic marketplace
</a>
:
</p>
<CodeBlock
value={`omp plugin marketplace add anthropics/claude-plugins-official\nomp plugin install supabase@claude-plugins-official`}
language="bash"
focusable={false}
className="block"
/>
<p className="text-xs text-foreground-lighter">
Installs with <code>--scope user</code> by default, making it available across all your
projects. Use <code>--scope project</code> to install it for the current project only.
</p>
<p className="text-xs text-foreground-lighter">
Inside a session, run <code>/marketplace</code> to browse plugins, then{' '}
<code>/reload-plugins</code> after installing to load the skills and MCP server.
</p>
</div>
)
}
if (client.key === 'vscode') {
return (
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<defs>
<linearGradient id="omp-mark" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#ed4abf" />
<stop offset=".5" stop-color="#9b4dff" />
<stop offset="1" stop-color="#5ad8e6" />
</linearGradient>
</defs>
<rect width="64" height="64" rx="12" fill="#0f0a14" />
<path fill="url(#omp-mark)" d="M14 16h36v8H40v32h-8V24h-6v22h-8V24h-4z" />
</svg>

After

Width:  |  Height:  |  Size: 471 B

+21
View File
@@ -6,6 +6,8 @@ import react from '@astrojs/react'
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'astro/config'
import supabaseTheme from '../learn/lib/themes/supabase-2.json' with { type: 'json' }
// Absolute dir of lodash-es, for the SSR-only lodash alias below (same fix
// apps/studio/vite.config.ts uses). `packages/ui`'s clipboard util does
// `import { noop } from 'lodash'` — under Vite's dev-mode SSR module runner,
@@ -45,4 +47,23 @@ export default defineConfig({
},
plugins: [tailwindcss(), ssrLodashEs],
},
markdown: {
shikiConfig: {
theme: supabaseTheme,
// Match the docs app's CodeBlock component (border + rounded-lg on the
// outer element), since Astro's own markdown pipeline renders code
// blocks straight to a `<pre>` with no wrapper we can add classes to.
transformers: [
{
name: 'kb-code-block-classes',
pre(node) {
this.addClassToHast(node, 'border border-default rounded-lg')
},
code(node) {
this.addClassToHast(node, 'inline-block p-6')
},
},
],
},
},
})
+7 -6
View File
@@ -9,15 +9,16 @@ import {
navigationMenuTriggerStyle,
} from 'ui'
import { TOPICS, topicToSlug } from '../lib/topics'
const topics = TOPICS.map((topic) => ({
label: topic.name,
href: `${import.meta.env.BASE_URL}/topics/${topicToSlug(topic.name)}`,
}))
/**
* Hard-codding links in here for now until we have actual content. Might be worth putting these arrays in their on data file too.
*/
const topics = [
{ label: 'Troubleshooting', href: '#' },
{ label: 'Migrations', href: '#' },
{ label: 'Comparisons', href: '#' },
]
const resources = [
{ label: 'Status', href: 'https://status.supabase.com' },
{ label: 'Changelog', href: 'https://supabase.com/changelog' },
+6 -2
View File
@@ -2,6 +2,8 @@ import { defineCollection } from 'astro:content'
import { glob } from 'astro/loaders'
import { z } from 'astro/zod'
import { TOPIC_NAMES } from './lib/topics'
// Every entry here is rendered through GuideLayout by
// src/pages/guides/[...slug].astro — dropping a new file in
// src/content/guides doesn't need any per-file layout wiring.
@@ -9,8 +11,10 @@ const guides = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/guides' }),
schema: z.object({
title: z.string(),
description: z.string().optional(),
topics: z.array(z.string()).optional(),
description: z.string(),
// z.enum (not z.string) so a guide referencing a topic outside TOPICS
// fails content validation instead of silently rendering an orphaned tag.
topics: z.array(z.enum(TOPIC_NAMES)),
github_url: z.string().optional(),
}),
})
+1 -1
View File
@@ -1,7 +1,7 @@
---
title: 'Markdown elements sample'
description: 'A lorem ipsum sample guide exercising every base Markdown element supported by the guide layout.'
topics: ['example', 'markdown']
topics: ['Tutorial', 'Database']
github_url: 'https://github.com/supabase/supabase/discussions/0000000'
---
+10 -2
View File
@@ -3,11 +3,12 @@ import { Github } from 'lucide-react'
import { Badge } from 'ui'
import Layout from './Layout.astro'
import { getTopicDescription, type Topic, topicToSlug } from '../lib/topics'
interface Props {
title: string
description?: string
topics?: string[]
topics?: Topic[]
github_url?: string
}
@@ -21,7 +22,14 @@ const { title, description, topics = [], github_url } = Astro.props
{
topics.length > 0 && (
<div class="flex flex-wrap gap-2">
{topics.map((topic) => <Badge>{topic}</Badge>)}
{topics.map((topic) => (
<a
href={`${import.meta.env.BASE_URL}/topics/${topicToSlug(topic)}`}
title={getTopicDescription(topic)}
>
<Badge>{topic}</Badge>
</a>
))}
</div>
)
}
+31
View File
@@ -0,0 +1,31 @@
// Canonical list of guide topics. Single source of truth for the `topics`
// field in src/content.config.ts (so a guide with an unsupported topic fails
// content validation), the "Topics" nav menu, and the /topics/[topic] pages.
export const TOPICS = [
{ name: 'Migration', description: 'Moving data, schemas, or projects onto Supabase' },
{ name: 'Comparison', description: 'How Supabase compares to other databases and platforms' },
{ name: 'Troubleshooting', description: 'Common errors and how to resolve them' },
{ name: 'Tutorial', description: 'Step-by-step walkthroughs for building with Supabase' },
{ name: 'Storage', description: 'Uploading, managing, and serving files' },
{ name: 'Auth', description: 'Authentication, authorization, and user management' },
{ name: 'Database', description: 'Postgres schemas, queries, and performance' },
{ name: 'Edge Functions', description: 'Deploying and running serverless functions' },
{ name: 'Queues', description: 'Background jobs and message processing' },
{ name: 'Realtime', description: 'Broadcast, presence, and database changes' },
{ name: 'Supabase Platform', description: 'Project settings, billing, and infrastructure' },
] as const
export type Topic = (typeof TOPICS)[number]['name']
// zod's `enum()` needs a literal non-empty tuple of strings, which `.map()`
// can't preserve on its own — this cast is safe because TOPICS is `as const`.
export const TOPIC_NAMES = TOPICS.map((topic) => topic.name) as [Topic, ...Topic[]]
export function getTopicDescription(topic: Topic): string {
return TOPICS.find((t) => t.name === topic)!.description
}
// URL-safe slug for a topic, e.g. "Edge Functions" -> "edge-functions".
export function topicToSlug(topic: Topic): string {
return topic.toLowerCase().replace(/\s+/g, '-')
}
+43
View File
@@ -0,0 +1,43 @@
---
import { getCollection } from 'astro:content'
import Layout from '../../layouts/Layout.astro'
import { TOPICS, topicToSlug } from '../../lib/topics'
export async function getStaticPaths() {
const guides = await getCollection('guides')
// One page per topic in TOPICS — not just topics a guide already uses —
// so every link on the "Topics" nav resolves instead of 404ing.
return TOPICS.map((topic) => ({
params: { topic: topicToSlug(topic.name) },
props: {
topic: topic.name,
description: topic.description,
guides: guides.filter((guide) => guide.data.topics?.includes(topic.name)),
},
}))
}
const { topic, description, guides } = Astro.props
---
<Layout title={topic} description={description}>
<article class="prose max-w-3xl mx-auto px-6 py-16">
<h1>{topic}</h1>
<p class="lead">{description}</p>
{
guides.length === 0 ? (
<p>No guides for this topic</p>
) : (
<ul>
{guides.map((guide) => (
<li>
<a href={`${import.meta.env.BASE_URL}/guides/${guide.id}`}>{guide.data.title}</a>
</li>
))}
</ul>
)
}
</article>
</Layout>
+31 -4
View File
@@ -73,8 +73,35 @@ h6:not(.font-mono),
border-color: var(--border-default, currentColor);
}
/* TEMPORARY UNTIL WE IMPLEMENT OUR OWN CODE COMPONENT */
.prose pre code {
padding: 1rem;
display: inline-block;
/* Code block theme colors for use with Supabase Theme */
[data-theme='dark'] {
--code-token-keyword: #bda4ff;
--code-foreground: #ffffff;
--code-token-constant: #3ecf8e;
--code-token-string: #ffcda1;
--code-token-comment: #949494;
--code-token-parameter: #ffffff;
--code-token-function: #3ecf8e;
--code-token-string-expression: #ffcda1;
--code-token-punctuation: #ffffff;
--code-token-link: #ffffff;
--code-token-number: #ffffff;
--code-token-property: #3ecf8e;
--code-highlight-color: #232323;
}
[data-theme='light'] {
--code-token-keyword: #5f2fc4;
--code-foreground: oklch(from var(--foreground-light) l c h / 1);
--code-token-constant: #15593b;
--code-token-string: #9a5200;
--code-token-comment: #6a6a6a;
--code-token-parameter: oklch(from var(--foreground-light) l c h / 1);
--code-token-function: #15593b;
--code-token-string-expression: #9a5200;
--code-token-punctuation: oklch(from var(--foreground-light) l c h / 1);
--code-token-link: oklch(from var(--foreground-light) l c h / 1);
--code-token-number: oklch(from var(--foreground-light) l c h / 1);
--code-token-property: #15593b;
--code-highlight-color: #1c1c1c;
}
@@ -171,6 +171,12 @@ export const useIsExplorerEnabled = () => {
return isExplorerEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_EXPLORER]
}
export const useIsStorageVersioningEnabled = () => {
const { flags } = useFeaturePreviewContext()
const isStorageVersioningEnabled = useFlag('storageVersioningPrivateAlpha')
return isStorageVersioningEnabled && flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_STORAGE_VERSIONING]
}
export const useFeaturePreviewModal = () => {
const featurePreviews = useFeaturePreviews()
const [featurePreviewModal, setFeaturePreviewModal] = useQueryState('featurePreviewModal')
@@ -39,6 +39,7 @@ import { JitDbAccessPreview } from './JitDbAccessPreview'
import { PgDeltaDiffPreview } from './PgDeltaDiffPreview'
import { PlatformWebhooksPreview } from './PlatformWebhooksPreview'
import { SqlEditorManualSavePreview } from './SqlEditorManualSavePreview'
import { StorageVersioningPreview } from './StorageVersioningPreview'
import { UnifiedLogsPreview } from './UnifiedLogsPreview'
import { FeaturePreview, useFeaturePreviews } from './useFeaturePreviews'
import { useBannerStack } from '@/components/ui/BannerStack/BannerStackProvider'
@@ -59,6 +60,7 @@ const FEATURE_PREVIEW_KEY_TO_CONTENT: {
[LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE]: <IntegrationsLayoutPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_DATABASE_CONNECTIONS]: <DatabaseConnectionsPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_EXPLORER]: <ExplorerPreview />,
[LOCAL_STORAGE_KEYS.UI_PREVIEW_STORAGE_VERSIONING]: <StorageVersioningPreview />,
}
export const FeaturePreviewModal = () => {
@@ -0,0 +1,23 @@
export const StorageVersioningPreview = () => {
return (
<div className="space-y-4">
<p className="text-sm text-foreground-light">
Keep previous copies of a file when it's overwritten or deleted, and set a lifecycle policy
that expires them automatically.
</p>
<div className="space-y-2">
<p className="text-sm">Enabling this preview will:</p>
<ul className="list-disc pl-6 text-sm text-foreground-light space-y-1">
<li>Add object versioning and lifecycle policy settings to the bucket settings</li>
<li>Show a version history for each file in the file preview panel</li>
<li>Allow soft-deleting and restoring files</li>
<li>Break down retained version storage on the organization usage page</li>
</ul>
</div>
<p className="text-sm text-foreground-light">
Versioning is in <em>Private Alpha</em> and is off for every bucket by default until you
turn it on.
</p>
</div>
)
}
@@ -36,6 +36,7 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
const isMarketplaceEnabled = useFlag('marketplaceIntegrations')
const isDatabaseConnectionsEnabled = useFlag('topForPostgres')
const isExplorerEnabled = useFlag('explorer')
const isStorageVersioningEnabled = useFlag('storageVersioningPrivateAlpha')
const isSqlEditorManualSaveForced = useFlag('sqlEditorManualSaveForced')
@@ -150,6 +151,16 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
getRoute: (ref?: string) => `/project/${ref}/observability/connections`,
bannerId: 'database-connections-banner',
},
{
key: LOCAL_STORAGE_KEYS.UI_PREVIEW_STORAGE_VERSIONING,
name: 'Storage versioning',
discussionsUrl: undefined,
isNew: true,
isPlatformOnly: true,
isDefaultOptIn: true,
enabled: isStorageVersioningEnabled,
getRoute: (ref?: string) => `/project/${ref}/storage/files`,
},
]
return previews.sort((a, b) => Number(b.isNew) - Number(a.isNew))
@@ -160,5 +171,6 @@ export const useFeaturePreviews = (): FeaturePreview[] => {
isMarketplaceEnabled,
isDatabaseConnectionsEnabled,
isExplorerEnabled,
isStorageVersioningEnabled,
])
}
@@ -29,6 +29,7 @@ export const LOCAL_STORAGE_KEYS = {
UI_PREVIEW_MARKETPLACE: 'supabase-ui-marketplace',
UI_PREVIEW_DATABASE_CONNECTIONS: 'preview-database-connections',
UI_PREVIEW_EXPLORER: 'preview-explorer',
UI_PREVIEW_STORAGE_VERSIONING: 'supabase-ui-storage-versioning',
AI_ASSISTANT_MCP_OPT_IN: 'ai-assistant-mcp-opt-in',
@@ -172,6 +173,7 @@ const LOCAL_STORAGE_KEYS_ALLOWLIST = [
LOCAL_STORAGE_KEYS.UI_PREVIEW_SQL_EDITOR_MANUAL_SAVE,
LOCAL_STORAGE_KEYS.SQL_EDITOR_MANUAL_SAVE_NOTICE_DISMISSED,
LOCAL_STORAGE_KEYS.UI_PREVIEW_MARKETPLACE,
LOCAL_STORAGE_KEYS.UI_PREVIEW_STORAGE_VERSIONING,
LOCAL_STORAGE_KEYS.LAST_SIGN_IN_METHOD,
LOCAL_STORAGE_KEYS.HIDE_PROMO_TOAST,
LOCAL_STORAGE_KEYS.BLOG_VIEW,
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">
<defs>
<linearGradient id="omp-mark" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#ed4abf" />
<stop offset=".5" stop-color="#9b4dff" />
<stop offset="1" stop-color="#5ad8e6" />
</linearGradient>
</defs>
<rect width="64" height="64" rx="12" fill="#0f0a14" />
<path fill="url(#omp-mark)" d="M14 16h36v8H40v32h-8V24h-6v22h-8V24h-4z" />
</svg>

After

Width:  |  Height:  |  Size: 471 B

@@ -13,6 +13,7 @@ import type {
McpClientConfig,
McpClientDeepLinkOptions,
McpFeatureGroup,
OmpMcpConfig,
OpenCodeMcpConfig,
VSCodeMcpConfig,
WindsurfMcpConfig,
@@ -360,6 +361,23 @@ export const MCP_CLIENT_DATA: McpClientData[] = [
}
},
},
{
key: 'omp',
label: 'omp',
icon: 'omp',
configFile: '.omp/mcp.json',
externalDocsUrl: 'https://github.com/can1357/oh-my-pi/blob/main/docs/mcp-config.md',
transformConfig: (config): OmpMcpConfig => {
return {
mcpServers: {
supabase: {
type: 'http',
url: config.mcpServers.supabase.url,
},
},
}
},
},
{
key: 'kiro',
label: 'Kiro',
@@ -449,6 +467,7 @@ export const MCP_CLIENT_GROUPS = [
'opencode',
'factory',
'fx',
'omp',
],
},
{
@@ -275,6 +275,32 @@ export const MCP_CLIENT_INSTRUCTIONS: Record<string, McpClientInstructions> = {
</>
),
},
omp: {
primary: () => (
<>
<paragraph>
Start <inlineCode value="omp" /> and add the Supabase MCP server with the guided setup:
</paragraph>
<code lang="bash" value="/mcp add" />
</>
),
alternate: () => (
<>
<paragraph>
That path is project-scoped. To use the server in every project, add the same entry to{' '}
<inlineCode value="~/.omp/agent/mcp.json" /> instead.
</paragraph>
<paragraph>
If a session is already open, pick up the change with <inlineCode value="/mcp reload" />.
omp opens your browser to complete the Supabase OAuth flow the first time it connects.
</paragraph>
<paragraph>
Confirm the server is connected with <inlineCode value="/mcp list" />, or authorize again
with a different account using <inlineCode value="/mcp reauth supabase" />.
</paragraph>
</>
),
},
kiro: {
deepLinkDescription: (
<paragraph>
@@ -214,6 +214,19 @@ export interface KimiMcpConfig extends McpClientBaseConfig {
}
}
}
/**
* Configuration format for the omp CLI MCP client.
* omp keys servers under `mcpServers` and requires an explicit `type: "http"`:
* an entry with a `url` but no `type` is validated as stdio and rejected.
*/
export interface OmpMcpConfig extends McpClientBaseConfig {
mcpServers: {
supabase: {
type: 'http'
url: string
}
}
}
// Union of all possible config types
export type McpClientConfig =
@@ -230,6 +243,7 @@ export type McpClientConfig =
| GrokMcpConfig
| KimiMcpConfig
| McpClientBaseConfig
| OmpMcpConfig
| OpenCodeMcpConfig
| OtherMcpConfig
| VSCodeMcpConfig
@@ -20,6 +20,7 @@ import grokIcon from '../assets/grok-icon.svg'
import kimiDarkIcon from '../assets/kimi-icon-dark.svg'
import kimiIcon from '../assets/kimi-icon.svg'
import kiroIcon from '../assets/kiro-icon.svg'
import ompIcon from '../assets/omp-icon.svg'
import openaiDarkIcon from '../assets/openai-icon-dark.svg'
import openaiIcon from '../assets/openai-icon.svg'
import opencodeDarkIcon from '../assets/opencode-icon-dark.svg'
@@ -52,6 +53,7 @@ const MCP_CLIENT_ICON_ASSETS = {
grok: { light: grokIcon, dark: grokDarkIcon },
kimi: { light: kimiIcon, dark: kimiDarkIcon },
kiro: { light: kiroIcon, dark: kiroIcon },
omp: { light: ompIcon, dark: ompIcon },
openai: { light: openaiIcon, dark: openaiDarkIcon },
opencode: { light: opencodeIcon, dark: opencodeDarkIcon },
perplexity: { light: perplexityIcon, dark: perplexityDarkIcon },