## What kind of change does this PR introduce?
Bug fix (dirty form dismissal for Replication destination sheets), plus
small docs/skill updates so agents pick up the existing modality
pattern.
## What is the current behavior?
Closing the Add/Edit destination sheet (Cancel, Escape, or backdrop)
discards in-progress form state with no confirm. Same for the nested
Create publication sheet.
## What is the new behavior?
Dirty closes go through `useConfirmOnClose` +
`DiscardChangesConfirmationDialog`, matching other Studio sheets.
Successful submit still closes without prompting.
Also: skills + `forms.mdx` now point at Modality “Dirty form dismissal”.
| After |
| --- |
| <img width="1024" height="759" alt="Replication Database Chisel
Toolshed Supabase"
src="https://github.com/user-attachments/assets/6f568a2a-c76b-442a-b592-d638bb36adc4"
/> |
### How to test
1. Studio → Database → Replication → **Add destination** (any pipelines
type with access).
2. Change a field so the form is dirty.
3. Try Cancel, Escape, and backdrop click → discard dialog appears;
**Keep editing** stays open; **Discard changes** closes.
4. Submit successfully with a valid config → sheet closes with no
discard dialog.
5. Repeat for **Edit destination** from a destination row menu.
6. Optional: Add destination → create a new publication from the
publication picker → dirty that nested sheet and dismiss the same way.
7. Optional: Add destination → Read Replica → change region → dismiss →
discard dialog; deploy still closes without prompting.
## Additional context
Sheet owns the close guard; forms report dirty via a ref because RHF
lives in the child. Nested `NewPublicationPanel` wires the guard
locally.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added unsaved-changes tracking to replication destination and
publication forms.
- Added confirmation prompts before closing forms with unsaved changes
via Cancel, Escape, or backdrop dismissal.
- Forms now reset appropriately after successful submission or confirmed
dismissal.
- **Documentation**
- Updated form and UI pattern guidance to document dirty-form dismissal
behavior for sheets and dialogs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
Feature + docs. Stacked on #48161 (logo contract /
[DEPR-604](https://linear.app/supabase/issue/DEPR-604/define-connect-logo-asset-and-variant-contract)).
## What is the current behavior?
After #48161, curated logos only resolve from allowlisted `redirect_uri`
hosts. A requester can still present a trusted partner **name** (e.g.
Claude) while redirecting to an unrelated remote host; the UI shows
Supabase alone but does not call out the mismatch.
## What is the new behavior?
- Shows a caution admonition when the requester name looks like a
trusted partner (Claude, Cursor, ChatGPT/OpenAI, Perplexity) but
`redirect_uri` is a **remote** host outside that partner's allowlist.
- Skips localhost / loopback redirects for the caution (common for local
MCP clients); those still get curated logos when the name matches a
trusted partner.
- Highlights the footer redirect URL in warning colour when the caution
is shown.
- Documents the behaviour in the Connect interstitials pattern.
### To test
Real MCP clients (Claude, Cursor, etc.) only send users to
**production** `/authorize`, so you cannot drive a local or preview
Studio build from those tools. Use a Network override instead:
1. Start Studio and sign in (`pnpm dev:studio`, or use the [Vercel
preview](https://studio-staging-git-danny-oauth-impersonation-warning-supabase.vercel.app/)).
2. Open `/dashboard/authorize?auth_id=foo` (any `auth_id` is fine; the
real response may 404) ([Vercel
preview](https://studio-staging-git-danny-oauth-impersonation-warning-supabase.vercel.app/dashboard/authorize?auth_id=foo)).
3. DevTools → **Network** → find `GET
…/platform/oauth/authorizations/foo` (or whatever id you used).
4. Right-click → **Override content** (enable Local Overrides / pick a
folder if prompted).
5. Paste one of the payloads below (status **200**), save, then reload
the authorize page.
6. Keep `expires_at` in the future so the request does not look expired.
#### Impersonation caution (trusted name + remote non-allowlisted
redirect)
Expect:
- Supabase alone (no curated Claude mark)
- Caution: “Redirect does not match this app name”
- Footer redirect URL in warning colour
```json
{
"name": "Claude",
"website": "https://claude.ai",
"icon": null,
"domain": "claude.ai",
"redirect_uri": "https://evil.com/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
| Preview |
| --- |
| <img width="764" height="958" alt="Authorize Claude Supabase"
src="https://github.com/user-attachments/assets/e6eee016-5710-41ba-9925-87511e009e22"
/> |
#### Localhost MCP: no caution
Expect curated Claude + Supabase pair (name match + loopback), **no**
caution, normal footer colour. Local MCP clients often use loopback
redirects.
```json
{
"name": "Claude",
"website": "https://claude.ai",
"icon": null,
"domain": "claude.ai",
"redirect_uri": "http://127.0.0.1:42813/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
| Preview |
| --- |
| <img width="764" height="958" alt="Authorize Claude Supabase"
src="https://github.com/user-attachments/assets/79f36865-3c8e-43e5-9490-24288efc74aa"
/> |
#### Legitimate curated partner: no caution
Expect curated Cursor + Supabase pair, no admonition, normal footer
colour.
```json
{
"name": "Cursor",
"website": "https://cursor.com",
"icon": null,
"domain": "cursor.com",
"redirect_uri": "https://cursor.com/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
| Preview |
| --- |
| <img width="764" height="958" alt="56164"
src="https://github.com/user-attachments/assets/412333a3-a74f-42eb-9f63-d56b6a26bf91"
/> |
#### Unrelated name + remote redirect: no caution
Expect Supabase alone (no icon), no admonition.
```json
{
"name": "Acme Tools",
"website": "https://evil.com",
"icon": null,
"domain": "evil.com",
"redirect_uri": "https://evil.com/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
| Preview |
| --- |
| <img width="764" height="958" alt="Authorize Acme Tools Supabase"
src="https://github.com/user-attachments/assets/dab24817-5c26-4aa1-a447-796c4af5868b"
/> |
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary by CodeRabbit
- **New Features**
- Added an OAuth caution when a requester name matches a known partner
but uses an unapproved remote redirect host.
- Improved trusted partner logo selection for localhost/loopback
redirects while preserving safe fallbacks for untrusted redirects.
- **Documentation**
- Updated Connect interstitial guidance for redirect mismatches and
localhost/loopback behavior.
- **Tests**
- Expanded coverage for caution visibility, messaging, localhost logo
pairing, and trusted redirect scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Bug fix and design-system update.
## What is the current behavior?
API authorisation and AWS Marketplace action failures use transient
toasts. The inline action-error treatment introduced for organisation
invitations is implemented locally.
## What is the new behavior?
Action failures remain visible below their actions and clear on retry or
organisation change.
This PR adds a shared `InterstitialActionError` component, updates the
connect-interstitial guidance and demo to use it, and retroactively
applies it to `OrganizationInvite`.
Mutation errors are read directly from their mutation hooks rather than
copied into component state.
| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Authorize API Access Supabase"
src="https://github.com/user-attachments/assets/9520aff3-496d-44b1-b5b5-02b331872e32"
/> | <img width="1024" height="759" alt="Authorize API Access Supabase"
src="https://github.com/user-attachments/assets/2d09e337-573a-45b5-80ac-7c546ed1401d"
/> |
| <img width="1024" height="759" alt="Link AWS Marketplace Supabase"
src="https://github.com/user-attachments/assets/bb1a4581-0399-432a-8037-d84ab15ecc4b"
/> | <img width="1024" height="759" alt="Link AWS Marketplace Supabase"
src="https://github.com/user-attachments/assets/f9d43cd9-c661-42ed-91c0-e45ccb9c19f5"
/> |
_Note since taking that AWS screenshot: the error message now replaces
the prior footer text. I.e. “Learn more about billing through AWS.” is
now gone when an error message is present._
## To test
### AWS Marketplace
For a visual check with local Studio running:
1. In
`apps/studio/components/interfaces/Organization/CloudMarketplace/AwsMarketplaceOnboarding.tsx`,
immediately before `if (!buyerId)`, temporarily add:
```tsx
return (
<AwsMarketplaceInterstitial>
<div className="flex flex-col gap-5">
<InterstitialAccountRow displayName="reviewer@example.com" />
<OrganizationSelector
organizations={[
{
name: 'Example Organization',
slug: 'example-organization',
plan: { id: 'pro', name: 'Pro' },
} as Organization,
]}
selectedSlug="example-organization"
disabled
onSelect={() => undefined}
/>
<div className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<Button variant="primary" block>
Link organization
</Button>
<InterstitialActionError error="Failed to link organization: Test error"
/>
</div>
<p className="text-center text-xs text-foreground-lighter text-balance">
<InlineLink href={`${DOCS_URL}/guides/platform/aws-marketplace`}>
Learn more
</InlineLink>{' '}
about billing through AWS.
</p>
</div>
</div>
</AwsMarketplaceInterstitial>
)
```
2. Open `http://localhost:8082/aws-marketplace-onboarding?buyer_id=test`
while signed in.
3. Confirm the error appears below **Link organization** with a divider.
Remove the temporary return before committing anything.
### API authorization
For a visual check with local Studio running:
1. In
`apps/studio/components/interfaces/ApiAuthorization/ApiAuthorization.Valid.tsx`,
immediately before `if (isLoading)`, temporarily add:
```tsx
return (
<ApiAuthorizationMainView
approvalState="indeterminate"
form={form}
requester={{
name: 'Test App',
website: 'https://example.com',
icon: null,
domain: 'example.com',
scopes: [],
expires_at: '2099-01-01T00:00:00.000Z',
approved_at: null,
registration_type: 'static',
}}
organizations={{
_tag: 'success',
organizations: [
{ name: 'Example Organization', slug: 'example-organization' } as
Organization,
],
}}
requestedOrganizationSlug={undefined}
actionError="Failed to authorize request: Test error"
onOrganizationChange={() => undefined}
onApprove={() => undefined}
onDecline={() => undefined}
/>
)
```
2. Open `http://localhost:8082/authorize?auth_id=test` while signed in.
3. Confirm the error appears below the authorisation actions with a
divider.
Remove the temporary return before committing anything.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added consistent inline error messaging for authorization,
organization invitations, and AWS Marketplace onboarding.
* Error messages now appear within the relevant interstitial and replace
supporting footer content until resolved.
* Retry and action buttons remain available after failed operations.
* **Bug Fixes**
* AWS Marketplace linking failures no longer trigger toast
notifications.
* Billing guidance is hidden while an onboarding error is displayed.
* **Tests**
* Added coverage for authorization, cancellation, and AWS Marketplace
failure states.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
Bug fix and design-system documentation update.
## What is the current behavior?
Invite acceptance failures only appear in a transient toast.
## What is the new behavior?
Invite failures remain visible beside the actions. The design-system
guidance now distinguishes field, action, state, and toast feedback.
| Before | After |
| --- | --- |
| <img width="759" height="619" alt="Join Organization Supabase"
src="https://github.com/user-attachments/assets/ed8e974c-5da3-477a-81da-628d3f847131"
/> | <img width="741" height="768" alt="Join Organization Supabase"
src="https://github.com/user-attachments/assets/4c3f6bcd-4ed9-40b2-8280-e8c8a44ecbd6"
/> |
## To test
With local Studio running at `http://localhost:8082`:
1. Open
`apps/studio/components/interfaces/OrganizationInvite/OrganizationInvite.utils.ts`.
2. At line 37, immediately inside `getOrganizationInviteStatus`, add:
```tsx
return 'ready'
```
This deliberately bypasses invite lookup and account checks for the
visual test.
3. Open
`apps/studio/components/interfaces/OrganizationInvite/OrganizationInvite.tsx`.
4. At line 30, change:
```tsx
const [joinError, setJoinError] = useState<string>()
```
to:
```tsx
const [joinError, setJoinError] = useState<string>('Invite token can
only be accepted via an SSO account')
```
5. Open `http://localhost:8082/join?token=test&slug=test` while signed
in.
6. Confirm the card says **Join an organization** and shows the error
below **Decline**, separated from the actions by a divider.
7. Revert both temporary edits before committing anything.
## Additional context
First PR in a five-PR stack.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added a new connect interstitial example showcasing an inline
action-error state with clear retry guidance.
- **Bug Fixes**
- Invitation acceptance failures now show inline destructive feedback
under “Accept invite,” keeping the button enabled for retry (and
removing prior toast-based failure behavior).
- Updated the invalid-invitation title to “Invalid invitation.”
- Changed the “Decline” link destination to `/organizations`.
- **Documentation**
- Expanded Sonner toast “When to use” guidance.
- Refined form and connect interstitial action-feedback patterns (inline
vs toast usage).
- **Tests**
- Updated and added coverage for the inline error rendering and “Invalid
invitation” text.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
Feature / a11y polish
## What is the current behavior?
Studio and Docs each had their own skip-to-content link (different
styling and behaviour). www and design-system had none.
## What is the new behavior?
Shared `SkipToContent` in `ui-patterns`, adopted by Studio, Docs, www,
and design-system. Documented as a fragment with a short note under
Accessibility → Jumping ahead.
Tab once to reveal the button (top-left), Enter to jump to a
content-only `<main>`.
| After |
| --- |
| <img width="836" height="324" alt="CleanShot 2026-07-24 at 14 08
47@2x"
src="https://github.com/user-attachments/assets/6df29452-e53a-4eca-8f64-946f2b9f605d"
/> |
## To test
Shared steps for every app: enable Tab key navigation if needed, load
the preview, press **Tab** once — skip button should slide in top-left.
Press **Enter** — focus jumps to main content (no blue ring on
`<main>`). Press **Tab** again — first interactive control in the page
body, not the sidebar/nav. Hover the skip button — solid fill, clear
hover state, no chrome showing through.
- **Studio** —
[preview](https://studio-staging-git-dnywh-featskip-to-content-supabase.vercel.app)
→ sign in → any project page
- **Docs** —
[preview](https://docs-git-dnywh-featskip-to-content-supabase.vercel.app)
→ any docs page with sidebar
- **www** —
[preview](https://zone-www-dot-com-git-dnywh-featskip-to-content-supabase.vercel.app)
→ homepage or any marketing page with the default nav
- **Design system** —
[preview](https://design-system-git-dnywh-featskip-to-content-supabase.vercel.app)
→ any docs page (confirm Tab from content does **not** walk the
sidebar), plus [Skip to Content
fragment](https://design-system-git-dnywh-featskip-to-content-supabase.vercel.app/docs/fragments/skip-to-content)
## Additional context
Follow-up to #47694 / #48303 (Studio) and #47515 (Docs).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Added a reusable “Skip to content” accessibility link across key
layouts and pages.
- Updated main landmarks to support keyboard focus and skip-link
navigation (`id="main"`).
- **Accessibility**
- Skip links now follow consistent landmark-target conventions and
remain hidden until focused.
- Improved documentation for skip links/jump shortcuts in persistent
chrome layouts.
- **Documentation**
- Added a dedicated Skip to Content fragment, navigation entry, and
expanded accessibility guidance.
- Updated button description wording in component docs.
- **Tests**
- Added component tests for SkipToContent.
- **Chores**
- Exposed SkipToContent via additional public package entry points.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Feature + docs. Closes
[DEPR-604](https://linear.app/supabase/issue/DEPR-604/define-connect-logo-asset-and-variant-contract).
## What is the current behavior?
`/authorize` logo resolution trusted self-asserted requester `name` (and
similar) for curated MCP marks, fell back to a letter tile when there
was no usable icon, and always used theme-reactive tile chrome. This
includes the scenario when pairing against unclassified uploaded OAuth
app bitmaps.
## What is the new behavior?
- [Documents the Connect logo asset/variant
contract](https://design-system-git-danny-depr-604-connect-logo-contract-supabase.vercel.app/design-system/docs/ui-patterns/connect-interstitials#logos)
(default to light, keep pairs matched, no theme-recolour of vendor
SVGs).
- Resolves curated partner logos from allowlisted `redirect_uri` hosts
only (`claude.ai` / `anthropic.com`, `cursor.com` / `cursor.sh`,
`chatgpt.com` / `openai.com`, `perplexity.ai`).
- Unknown / missing / failed requester icons show `SupabaseLogo` alone
(no letter tile).
- Uploaded organisation OAuth app icons (unclassified bitmaps) pair with
fixed light tile chrome (`border-black/10 bg-white` / `SupabaseLogo
forceLight`) on both sides across Studio themes.
- Curated partners keep theme-reactive tiles and may use dark assets
when available.
### To test
Real MCP clients (Claude, Cursor, etc.) only send users to
**production** `/authorize`, so you cannot drive a local or preview
Studio build from those tools. Use a Network override instead:
1. Start Studio and sign in (`pnpm dev:studio`, or use the Vercel
preview once available).
2. Open `/dashboard/authorize?auth_id=foo` (any `auth_id` is fine — the
real response may 404).
3. DevTools → **Network** → find `GET
…/platform/oauth/authorizations/foo` (or whatever id you used).
4. Right-click → **Override content** (enable Local Overrides / pick a
folder if prompted).
5. Paste one of the payloads below (status **200**), save, then reload
the authorize page.
6. Keep `expires_at` in the future so the request does not look expired.
The fields that matter for this PR are `name`, `icon`, and
`redirect_uri`.
#### Curated pair (allowlisted redirect)
Expect Cursor mark + Supabase pair. Toggle light/dark: curated dark
assets may swap; tiles stay theme-reactive (`bg-surface-75`).
```json
{
"name": "Cursor",
"website": "https://cursor.com",
"icon": null,
"domain": "cursor.com",
"redirect_uri": "https://cursor.com/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
#### Unknown → Supabase alone
Expect Supabase bolt alone. No letter tile. No curated mark even if
`name` says Claude.
```json
{
"name": "Acme",
"website": "https://acme.example",
"icon": null,
"domain": "acme.example",
"redirect_uri": "https://acme.example/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
#### Spoofed trusted name, non-allowlisted redirect (logo only)
Expect Supabase alone (no Claude mark). This PR does **not** show the
impersonation caution (that is coming in #48162).
```json
{
"name": "Claude",
"website": "https://claude.ai",
"icon": null,
"domain": "claude.ai",
"redirect_uri": "https://evil.com/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "dynamic"
}
```
#### Uploaded OAuth app icon → forced-light pair
Expect remote icon + Supabase pair with forced-light tiles
(`border-black/10 bg-white`) on both sides in light and dark Studio
themes. The icon URL below is the checked-in solid-colour Acme bitmap on
this branch.
```json
{
"name": "Acme",
"website": "https://acme.example",
"icon": "https://raw.githubusercontent.com/supabase/supabase/danny/depr-604-connect-logo-contract/apps/design-system/public/img/icons/acme-oauth-icon.png",
"domain": "acme.example",
"redirect_uri": "https://acme.example/callback",
"expires_at": "2099-01-01T00:00:00.000Z",
"scopes": ["organizations:read", "projects:read"],
"approved_at": null,
"registration_type": "static"
}
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Improved authorization interstitial branding with trusted requester
logos and safer fallback behavior.
* Added support for consistent light-theme treatment of uploaded OAuth
app icons.
* Added examples and documentation for unknown requesters, uploaded
logos, and wrong-account states.
* **Bug Fixes**
* Prevented unverified or unavailable requester icons from being
presented as trusted.
* Ensured logo pairing remains visually consistent across light and dark
themes.
* **Tests**
* Added coverage for trusted-host validation, fallback branding, icon
loading failures, and theme behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## Summary
- Adds design-system guidance for the shared Connect interstitial layout
used by authorisation, invite, marketplace, CLI, and credit flows
- Includes a glanceable example showing the centred 400px card for
partner authorise and wrong-account invite states
- Documents Studio helpers (`InterstitialLayout`, logo helpers, account
row, `OrganizationSelector`) so future surfaces reuse one pattern
instead of bespoke shells
## Context
Most of the Studio Connect UI work from this effort has already landed.
This PR keeps the documentation and design-system example so the pattern
stays discoverable.
Related: [Shared Connect UI for Authorization and Partner
Flows](https://linear.app/supabase/project/shared-connect-ui-for-authorization-and-partner-flows-94587ac29d38)
## Test plan
- [ ] Open `/docs/ui-patterns/connect-interstitials` in the design
system
- [ ] Confirm the page appears under UI Patterns in the nav
- [ ] Confirm the example renders the authorise and wrong-account cards
side by side
- [ ] Skim the guidance for accuracy against current Studio
`InterstitialLayout` usage
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added new design-system “Connect Interstitials” example demos,
including branding variations (single vs dual logos) and a complete
connect-card flow with account row and sign-out action.
* Registered the new Connect Interstitials examples in the design-system
example registry.
* **Documentation**
* Added a “Connect Interstitials” UI Patterns page covering when to use
the pattern, recommended card/layout structure, branding/logo guidance,
and conventions for states, actions, and copy.
* Updated the documentation sidebar to include the new page.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
Our `<Button>` component breaks the default `button` contract by
redefining the `type` prop to set its variant (`primary`, `default`,
etc) instead of the button type (`submit`, `button`, etc).
This is confusing and forces to write more code when using it with
shadcn components that expect/inject the standard button props.
## Solution
- rename the `type` prop to `variant`
- rename the `htmlType` prop to `type`
- propagate the changes where necessary
- format code
## How to test
As this is just prop renaming, if it builds it's ok
---------
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
- Marketplace index page
- update order of feature partner integrations in hero
- fix z-index on MarketplaceFilterBar in "list" view
<img width="275" height="104" alt="Screenshot 2026-06-02 at 17 07 29"
src="https://github.com/user-attachments/assets/5cef64f9-895e-4f8d-8f30-153ddd5c89dd"
/>
- Marketplace detail page
- use "prose" css styling on overview content for better text styling
(heading with top padding, etc)
- refine FilesView in overview tab to only show swipeable and zoomable
previews (so the big image doesn't occupy too much space) + lazy load
FilesView component
- improve page loading state
- improve overview side rail sticky-top and remove redundant "About"
label
<img width="1333" height="732" alt="Screenshot 2026-06-02 at 17 20 29"
src="https://github.com/user-attachments/assets/8f3dd4a0-c241-4b7f-b8c8-192e1d7a616d"
/>
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Interactive carousel with image zoom capability for viewing
integration preview images
* **Bug Fixes**
* Fixed z-index layering issue with marketplace filter bar
* **Refactor**
* Redesigned marketplace detail page header with breadcrumb navigation
* Updated integration image handling structure with enhanced metadata
* Optimized dynamic loading for integration file viewers
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## What kind of change does this PR introduce?
Add `Markdown` component
- as a ui-pattern
- docs in design-system
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Introduced a Markdown UI pattern with full documentation and a docs
sidebar entry.
* Markdown component supports composable renderers, optional lazy syntax
highlighting, and customizable element overrides.
* Added many interactive examples (full demo, headings, paragraphs,
lists, links, inline code, blockquotes, code blocks, tables, images,
horizontal rules) plus Quote and Avatar demos.
* **Documentation**
* New MDX guide detailing usage, props, and customization previews.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46325?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Adjusts layout / page patterns including design system documentation and
page components.
<img width="937" height="643" alt="image"
src="https://github.com/user-attachments/assets/5f7864a3-04ae-4ccf-b2b8-d9fe5e29554e"
/>
**To test**
- Ensure all existing page types in Studio are un-effected. They
shouldn't be since existing components aren't touched.
- Run design-system and have a read through layout and page header
documentation. Make sure you understand when and where to use the
various combinations of page components and their variants.
**Next**
Once we are happy with these patterns we can gradually start to update
Studio pages that make use of breadcrumbs and/or sub navigation.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added PageBreadcrumbs and PageNav page chrome
* Several new interactive layout examples (Auth Emails, Edge Functions,
Full‑Width, Logs) and demo pages
* **Documentation**
* New docs for PageBreadcrumbs and PageNav
* Reworked layout guidance and patterns; updated fragment/example docs
and usage notes
* **Improvements**
* Component preview now supports explicit padding control
* Simplified page header demo and updated previews
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46126?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
We used to have a `_Shadcn_` suffix for all the shadcn form components
because we also had `formik` form components.
This is not needed anymore.
## Solution
- Remove the suffix
- Update all usages
## What kind of change does this PR introduce?
Design system and validation consistency update.
## What is the current behaviour?
`KeyValueFieldArray` already renders per-cell form messages, but each
consumer still decides its own validation rules. At the moment, some
consumers allow partially filled rows to submit silently, while Log
Drains now treats them as inline validation errors.
## What is the new behaviour?
This PR standardises the recommended partial-row behaviour for the
current `KeyValueFieldArray` consumers by introducing a shared
validation helper and using it from each form schema.
- adds `getKeyValueFieldArrayValidationIssues` alongside
`KeyValueFieldArray`
- keeps `KeyValueFieldArray` presentation-only and leaves validation in
consumer schemas
- shows inline errors when one side of a key/value row is filled and the
other is empty
- keeps fully empty rows as draft rows
- keeps duplicate-key validation in Log Drains, where it already applies
- updates the design-system docs and examples to describe the validation
pattern explicitly
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added reusable key/value validation utilities and public export; forms
now trim header/key/value inputs, show inline errors for partially
filled rows, and remove fully empty draft rows on submit.
* **Documentation**
* Clarified the field-array is rendering-only and added guidance for
placing validation in form schemas and handling draft rows.
* **Tests**
* Added unit and integration tests covering validation rules, duplicate
keys, trimming, draft-row stripping, and payload behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Chore that resolves DEPR-394.
## What is the current behavior?
This stack is consolidating RHF field-array patterns in smaller
reviewable steps.
After the key/value work lands, repeated single-value inputs are still
implemented separately across auth and SSO forms, and the design-system
docs only document the key/value pattern.
## What is the new behavior?
- adds a shared `SingleValueFieldArray` component in `ui-patterns`
- migrates repeated single-value inputs in:
- Redirect URL allow-list modal
- OAuth app redirect URIs
- SSO domains
- SSO attribute mapping
- documents the single-value pattern in the design system with:
- a dedicated fragment page
- updated forms guidance
- updated form pattern demos
- adds focused redirect URL modal coverage
## Additional context
This is PR 3 of a 3-PR stack for DEPR-394.
Base PR: #44058
## Problem
The design system documentation and examples promote an invalid usage of
react-hook-form state.
## Solution
- Fix the documentation and examples
- Update Claude skills for future components
## Out of scope
- Fix current code across the repository
## What kind of change does this PR introduce?
Chore that references DEPR-394.
## What is the current behavior?
Key/value editors for headers are implemented separately in multiple
places.
## What is the new behavior?
DEPR-394 is consolidating repeated RHF field-array UIs across Studio and
the design system.
- adds a shared `KeyValueFieldArray` component in `ui-patterns`
- adds a shared `httpHeaderAddActions` helper for preset header rows
- migrates the key/value header editors in:
- Platform Webhooks
- Cron Jobs HTTP headers
- Database Webhooks HTTP headers
- documents the key/value pattern in the design system with:
- a dedicated fragment page
- updated forms guidance
- updated form pattern demos
| Preview |
| --- |
| <img width="1102" height="420" alt="CleanShot 2026-03-23 at 12 22
18@2x"
src="https://github.com/user-attachments/assets/f8d23ff9-7063-462f-8074-b400561f77e9"
/> |
## Additional context
This is PR 1 of a 3-PR stack for DEPR-394.
## What kind of change does this PR introduce?
Bug fix. Resolves FE-2802. Fixes#43882.
## What is the current behavior?
Second-level foreign key side panels in Table Editor can be dismissed
while dirty without a discard confirmation. That makes accidental
dismissal easy and is inconsistent with the shared dirty-form dismissal
pattern from FE-2802.
## What is the new behavior?
- Adds `DiscardChangesConfirmationDialog` handling to
`ForeignKeySelector`
- Guards all selector close paths behind a local dirty-state check
- Normalizes foreign key state so derived type metadata does not create
false dirty prompts
- Clarifies in `modality.mdx` that route-driven/page-unload flows should
use a navigation guard rather than the dialog/sheet close guard
| Dirty State | Dialog |
| --- | --- |
| <img width="1502" height="899" alt="test Table Editor AWS Healthy
Toolshed Supabase-D1EA9D5F-46EB-4DB5-830D-7CEF36A55B7E"
src="https://github.com/user-attachments/assets/3d866850-d062-4fb7-9949-2fd713e6088f"
/> | <img width="1502" height="899" alt="test Table Editor AWS Healthy
Toolshed Supabase"
src="https://github.com/user-attachments/assets/1396361d-1d44-4206-862f-3379ea22be30"
/> |
## Additional context
Includes a focused unit test for foreign key dirty-state normalization.
This PR fixes some prettier issues:
- Bump and unify all prettier versions to 3.7.3 across teh whole repo
- Bump the SQL prettier plugin
- When running `test:prettier`, check `mdx` files also
- Run the new prettier format on all files
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
Refactor, cleanup, and docs update.
## What is the current behavior?
After the page-title rollout, `ProjectLayout` is still in a transitional
state:
- it accepts a deprecated `title` prop
- it still supports a separate `browserTitle.surface`
- wrapper layouts are split between passing `title` directly and passing
`browserTitle.section`
That makes the API harder to reason about than it needs to be, even
though the rendered titles are already correct.
## What is the new behavior?
This cleanup finishes the API simplification that came out of the
stacked PR review:
- wrapper layouts stay `title`-first for DX
- `ProjectLayout` no longer accepts `title`
- `product` is now the single source of truth for the project-surface
title segment
- `browserTitle` is now only used for extra browser-title metadata
(`entity`, `section`, `override`)
- the remaining project-scoped callers now pass `browserTitle.section`
when they need a section label
- docs now reflect the final pattern instead of the transitional one
Rendered page titles stay the same.
## Additional context
Checks run:
- `pnpm --filter studio exec vitest --run lib/page-title.test.ts
components/layouts/ProjectLayout/index.test.tsx`
- `pnpm --filter studio typecheck`
- `pnpm exec prettier --check ...` on touched files
This is intended as the post-rollout cleanup PR based on Joshen's review
feedback across the stacked title changes.
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Stacked PR 5/5 for page title improvements. Includes /new and
/new/[slug] title updates plus navigation docs note.
_Base:
[dnywh/feat/page-titles](https://github.com/supabase/supabase/pull/43538)_
---
## What kind of change does this PR introduce?
- Resolves FE-1960
- Resolves FE-1983
- Resolves DEPR-207
## What is the current behavior?
Page titles between surfaces are inconsistent and vague. Sometimes they
say the product name:
```
My Project | My Org | Supabase
```
...even when on a specific surface like Database > Tables.
Other times they show the entity name but skip over the project or org
name :
```
Edge Functions | Supabase
```
## What is the new behavior?
Includes /new and /new/[slug] title updates plus navigation docs note.
Adopts the layout title format introduced in
https://github.com/supabase/supabase/pull/43538:
```
users | Table Editor | My Project | My Org | Supabase
hello-world | Logs | Edge Functions | My Project | My Org | Supabase
Backups | Database | My Project | My Org | Supabase
Authentication | My Project | My Org | Supabase
```
That format is:
entity, section, surface, project, org, brand
## Additional context
Related stacked PRs also based on the the original
[dnywh/feat/page-titles](https://github.com/supabase/supabase/pull/43538)
branch:
- https://github.com/supabase/supabase/pull/43534
- https://github.com/supabase/supabase/pull/43535
- https://github.com/supabase/supabase/pull/43536
## What kind of change does this PR introduce?
Form handling improvement.
## What is the current behavior?
https://github.com/supabase/supabase/pull/43201/ standardised our
discard changes behaviour with a shared hook and
`DiscardChangesConfirmationDialog` component. But many forms and sheets
still:
1. Don’t have any Discard-confirm close behaviour, making it too easy to
make accidental discards
2. Use a more complicated, manually-created `CloseConfirmationModal`
approach
## What is the new behavior?
- Replaced all instances of `#2` above that had `CloseConfirmationModal`
with `DiscardChangesConfirmationDialog` and its hook
- Improved design system documentation around dirty form dismissal
| Before | After |
| --- | --- |
| <img width="987" height="569" alt="Mercor Apexroles Foo
Supabase-9A40EC7C-F335-4B26-B567-450FC0845463"
src="https://github.com/user-attachments/assets/363bed82-34d2-4cc8-9164-6d18cfdbdbbc"
/> | <img width="987" height="569" alt="Mercor Apexroles Foo
Supabase-F427F1FA-DECC-4194-B663-A9E5A6F285A1"
src="https://github.com/user-attachments/assets/d49fafdc-a5c2-46df-9b67-ec42bacbe716"
/> |
## To test
Try editing values these sheets in staging, then blurring the sheet or
pressing `esc`:
- CreateQueueSheet.tsx
- CronJobsTab.tsx
- CronJobPage.tsx
- EditWrapperSheet.tsx
- OverviewTab.tsx
- WrappersTab.tsx
- CreateFunction/index.tsx
- EditHookPanel.tsx
- TriggerSheet.tsx
- SidePanelEditor.tsx
- EditSecretSheet.tsx
- PolicyEditorModal/index.tsx
- PolicyEditorPanel/index.tsx
## Still to come
- [ ] Incrementally take on `#1`: implement
`DiscardChangesConfirmationDialog` and its hook in sheets or dialog
forms that have no dirty form dismissal handling
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
UX consistency improvement. Updates DEPR-355.
## What is the current behavior?
Discard-confirm close behavioir is implemented inconsistently across
Studio forms:
- some sheets/dialogs used `useConfirmOnClose`
- some duplicated local `CloseConfirmationModal` components
- some (e.g. `CreateHookSheet`) closed unconditionally and could lose
unsaved changes
## What is the new behavior?
Extracts and validates a reusable discard-close pattern for
dialogs/sheets
- enhances `useConfirmOnClose` with `handleOpenChange(open)` for
`Dialog`/`Sheet` `onOpenChange`
- adds shared `DiscardChangesConfirmationDialog` (`AlertDialog`-based,
override-able copy)
- migrates:
- `InviteMemberButton`
- `CreateHookSheet`
- `EditSecretSheet`
This standardizes close-guard behavior for
backdrop/escape/close-button/cancel-button flows without trying to block
route changes or arbitrary unmounts.
## Additional context
`CreateHookSheet` now also marks the generated secret action as dirty
(`setValue(..., { shouldDirty: true })`) so the discard guard behaves
correctly.
- Added tests for `useConfirmOnClose` covering:
- clean vs dirty close
- handleOpenChange(true|false)
- confirm/cancel behavior
- latest callback ref behavior
A follow-up PR is needed to migrate remaining duplicated
`CloseConfirmationModal` usages and older `useConfirmOnClose` call sites
to the shared `DiscardChangesConfirmationDialog` + `handleOpenChange`
pattern.
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## What kind of change does this PR introduce?
- ~Feat~ Chore
- Resolves DEPR-33
## What is the current behavior?
The dialog used to delete an organization uses a ”type this org’s name
in before you can press this button” interaction but:
- A) Enables the button the whole time
- B) We already have this componentized in TextConfirmModal
## What is the new behavior?
- Refactored this dialog to use `TextConfirmModal` instead
- Updated design docs to make this prop clearer
| Before | After |
| --- | --- |
| <img width="1024" height="759" alt="Supabase"
src="https://github.com/user-attachments/assets/85c965f6-29d4-4696-8c0c-a54ddcbe6167"
/> | <img width="1024" height="759" alt="Supabase"
src="https://github.com/user-attachments/assets/0a96ca65-15c3-4b1b-bb10-620526c059b8"
/> |
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Updated Text Confirm Dialog documentation with clarifications: the
confirm action remains disabled until input exactly matches the required
string, and leading/trailing whitespace is trimmed from the input.
* **Refactor**
* Streamlined the organization deletion confirmation workflow for
improved user experience.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## What kind of change does this PR introduce?
Bug fix
## What is the current behavior?
- Links on the UI Patterns introduction page are wrong and therefore 404
- Introduction pages on the sidebar are also wrong and 404
## What is the new behavior?
Fixes for both of the above
## Additional context
Thanks [Emilio](https://github.com/supabase/supabase/pull/42309) for
letting us know!
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Documentation**
* Updated navigation structure in the design system documentation to
direct users to introduction pages for UI Patterns, Fragments, and
Components sections.
* Refined internal documentation links for improved navigation
consistency.
<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
* feat: remove unused chart files
* chore: regenerate registry
* fix: logs bar chart positioning
* feat: add charts page to ui patterns
* chore: alphabetise ui patterns nav
* feat: base composable chart components
* chore: prettier for charts.mdx
* chore: slowly putting example together
* feat: add chart header components
* feat: add chart content wrapper
* feat: add recharts to source
* feat: add empty state handling
* feat: add loading state for content area as well
* feat: starting on actual recharts pieces
* feat: add basic api to documentation
* feat: add disabled state too
* feat: couple of basic chart types
* chore: tidy up for now
* feat: modify eslint rules for our demo files
* fix: recharts logo colour on light mode
* feat: absorb metric card into chart
* feat: align prop for chart header
* feat: refactor chart content
* feat: example with table
* chore: format for prettier
* fix: pkg lock merge conflict
* fix: small bits from danny
* feat: add link in ui pattern intro to charts
* chore: remove charts link from atoms
* chore: update metric padding to saxon suggestion
* feat: table footer overrides by default
* feat: update api props in table
* chore: formating
* feat: sort intro page entry points and sidebar
* feat: beef up getting started intro and faq
* chore: small copy fix
* feat: description for ui patterns intro
* feat: basic description for fragments intro
* feat: add description for atom components intro
* feat: add links to ui pattern docs
* chore: run prettier for mdx files
* suppress webpack/Turbopack warning
* empty states first draft
* todo note
* empty state documentation
* remove empty code block and confusing examples