Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,12 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
href: '/contributing' as `/${string}`,
enabled: contributionEnabled,
},
{
label: 'Troubleshooting',
icon: 'troubleshooting',
href: '/guides/troubleshooting' as `/${string}`,
level: 'troubleshooting',
},
],
],
},
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/data/content-listings/ai-tools.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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".
Expand Down
8 changes: 8 additions & 0 deletions apps/docs/features/ui/AgentPluginsPanel.data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
32 changes: 32 additions & 0 deletions apps/docs/features/ui/AgentPluginsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
11 changes: 11 additions & 0 deletions apps/docs/public/img/icons/agent-omp-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions apps/kb/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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')
},
},
],
},
},
})
45 changes: 45 additions & 0 deletions apps/kb/src/components/Card.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
// Small "glass panel" card, adapted from apps/docs' GlassPanel
// (packages/ui-patterns/src/GlassPanel/index.tsx) — same surface treatment
// (bg-surface-75, hover:border-strong), trimmed to what kb needs: no
// logo/icon-picker machinery, which pulled in next/image and next-themes
// that don't apply to a plain Astro app.
//
// Unlike GlassPanel (always a plain <div>, link-agnostic — every call site
// in docs wraps it in a Next.js <Link>), this renders as an <a> itself when
// `href` is given. That's only safe because `topics` render as plain,
// non-linked badges — nothing inside the card is itself a link, so there's
// no nested-<a> problem.
import { Badge } from 'ui'

interface Props {
href?: string
title: string
description?: string
topics?: string[]
class?: string
}

const { href, title, description, topics = [], class: className } = Astro.props
const Tag = href ? 'a' : 'div'
---

<Tag
href={href}
class:list={[
'relative flex h-full flex-col gap-2 rounded-lg border border-default bg-surface-75 p-5 text-left transition hover:border-strong',
className,
]}
>
<h2 class="m-0 text-base font-medium text-foreground">{title}</h2>
{description && <p class="m-0 text-sm text-foreground-light">{description}</p>}
{
topics.length > 0 && (
<div class="mt-2 flex flex-wrap gap-2">
{topics.map((topic) => (
<Badge>{topic}</Badge>
))}
</div>
)
}
</Tag>
2 changes: 1 addition & 1 deletion apps/kb/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ThemeToggle } from './ThemeToggle'
// search, no user menu, no dashboard link — kb doesn't have any of that yet.
function Header() {
return (
<header className="backdrop-blur-sm backdrop-filter bg-default/75 border-b w-full px-5 lg:pl-10 items-center flex h-(--header-height) gap-3">
<header className="backdrop-blur-sm backdrop-filter bg-default/75 border-b border-default w-full px-5 lg:pl-10 items-center flex h-(--header-height) gap-3">
<div className="flex basis-full h-full items-center justify-center gap-2">
<Logo />
<Nav />
Expand Down
2 changes: 1 addition & 1 deletion apps/kb/src/components/Hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// prompt panel (docs-specific, feature-flagged, and not something kb has yet).
function Hero() {
return (
<div className="w-full border-b bg-muted/10">
<div className="w-full border-b border-default bg-muted/10">
<div className="mx-auto max-w-7xl px-6 py-16">
<h1 className="m-0 text-3xl text-foreground sm:text-4xl">Supabase Knowledge Base</h1>
<p className="m-0 mt-2 text-base leading-7 text-foreground-light sm:mt-3 sm:text-xl">
Expand Down
13 changes: 7 additions & 6 deletions apps/kb/src/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand Down
10 changes: 8 additions & 2 deletions apps/kb/src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ 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.
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)),
// Surfaces the guide in the homepage's "Featured Guides" section.
pinned: z.boolean().default(false),
github_url: z.string().optional(),
}),
})
Expand Down
3 changes: 2 additions & 1 deletion apps/kb/src/content/guides/sample-guide.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
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']
pinned: true
github_url: 'https://github.com/supabase/supabase/discussions/0000000'
---

Expand Down
12 changes: 10 additions & 2 deletions apps/kb/src/layouts/GuideLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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>
)
}
Expand Down
55 changes: 55 additions & 0 deletions apps/kb/src/lib/topics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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.
//
// `pinned` topics surface as cards on the homepage (see src/pages/index.astro)
// — keep this to a handful so that section stays a highlights row, not a
// second copy of the full topic list.
export const TOPICS = [
{
name: 'Migration',
description: 'Moving data, schemas, or projects onto Supabase',
pinned: false,
},
{
name: 'Comparison',
description: 'How Supabase compares to other databases and platforms',
pinned: false,
},
{ name: 'Troubleshooting', description: 'Common errors and how to resolve them', pinned: false },
{
name: 'Tutorial',
description: 'Step-by-step walkthroughs for building with Supabase',
pinned: true,
},
{ name: 'Storage', description: 'Uploading, managing, and serving files', pinned: false },
{ name: 'Auth', description: 'Authentication, authorization, and user management', pinned: true },
{ name: 'Database', description: 'Postgres schemas, queries, and performance', pinned: true },
{
name: 'Edge Functions',
description: 'Deploying and running serverless functions',
pinned: false,
},
{ name: 'Queues', description: 'Background jobs and message processing', pinned: false },
{ name: 'Realtime', description: 'Broadcast, presence, and database changes', pinned: false },
{
name: 'Supabase Platform',
description: 'Project settings, billing, and infrastructure',
pinned: false,
},
] 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, '-')
}
Loading
Loading