## Problem
Admins should be able to revoke an OAuth app that uses scoped tokens.
## Solution
## Solution
- Add a menu to the org settings oauth approved apps rows to revoke the
app
<img width="1164" height="342" alt="image"
src="https://github.com/user-attachments/assets/1323318a-7c68-436e-ae77-0c79d763e4cc"
/>
<img width="441" height="350" alt="image"
src="https://github.com/user-attachments/assets/dd5cf88e-184a-4edb-a240-340bd2f58727"
/>
## Review instructions
In Org Settings/OAuth apps, you should see a menu button for each app
that contains a _Revoke_ item. Clicking this item should open a dialog
to revoke the app.
Notice how the focus is back on the dropdown trigger button when the
dialog is closed (canceled or success)
---------
Co-authored-by: kemal <hello@kemal.earth>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## Context
A project could already have a GitHub integration set up by another
member, which will show up in the branch management side navigation as
such:
<img width="259" height="255" alt="image"
src="https://github.com/user-attachments/assets/8bc12ef2-5f25-4245-9c89-4563b08a42a8"
/>
However if the user hasn't authenticated with GitHub, the project
settings -> integrations page will obfuscate the integration details
with a "Connect GitHub" CTA, which is a confusing UX as it hints that
there's no integration set up + conflicts with what's on the branch
management page
<img width="768" height="318" alt="image"
src="https://github.com/user-attachments/assets/a68409c0-f2db-407d-9bac-c109a0d14214"
/>
## Changes involved
Opting to show the integration details if there's one set up
irregardless of whether the user has authenticated with GH, but we'll
require the user to complete the authentication to manage the
integration settings as such (all input fields will also be disabled
until the auth is complete):
<img width="1031" height="511" alt="image"
src="https://github.com/user-attachments/assets/6fad387b-6e76-475e-b4e6-76972d8f76b6"
/>
Note that you can still disable the integration nonetheless irregardless
of the GH connection status
Also fixes the "Remove connection" CTA from Account Preferences - we
were invalidating the GH authorization query cache but the GH
authorization requests normally fails with a 404 if there's no auth set
up, so the query cache would remain stale. Opting to clear the query
cache instead of invalidating
## To test
- [ ] Set up a GH integration for your project under project settings ->
integrations
- [ ] Head to account preferences to remove your GH connection
- [ ] Go back to the project settings -> integrations page, should still
see the GH integration details, just unable to edit
- [ ] Connect with GH, and you should be able to edit the integration
details thereafter
## [5/10] Storage object versioning: version history in the file preview
panel
**Base:** `feat/storage-versioning/004-object-versions-data` #49207
### This PR
Adds all the versioning ui to the file preview panel.
- polished file preview panel (header, buttons, nits)
- added "Upload file" to the action buttons
- "Delete permanently" now defaults to "Archive" as primary action, and
"Delete permanently" via a dropdown menu as a more destructive action
- added "Versions" toggle section to the file preview panel
- with lifecycle policy "summary"
- with additional explanation on hover
- versions can be restored or deleted permanently
### How to test
- in a File storage bucket, open a file preview panel
- when the "Storage Versioning" feature preview flag is true, the
preview panel should show
- a "versions" section
- a new "upload file" button next to the "download file" button
- the "delete permanently" button should show "Archive" + a dropdown
with the "Delete permanently" option
- if the feature preview flag is toggled _off_, the preview panel should
looks as before (but with some minor polish)
https://github.com/user-attachments/assets/56f6b9aa-b3a0-45bd-b3e7-5788967d196a
With the feature preview toggled _on_:
- the ui should show the lifecycle policy "summary" that will affect
this file
- hovering the lifecycle policy summary should explain how the policy
will affect/clean up the noncurrent files
- the lifecycle policy summary hover card button should link and open
the "edit bucket modal"
- create a new version of a file by using the "Upload file" (next to
"download file")
- the file should have a new "Current" version
- the previous version should become a "noncurrent" version
- the noncurrent version should reflect the lifecycle policy that will
eventually affect it with some additional context on hover via a tooltip
- clicking on a noncurrent version should open a "comparison widget"
that provides a way to restore it and re-promote it to "current" or
delete it permanently
- these actions are also available via dropdown menu from the version
item
- a file should be "archiveable" (please note that showing/managing
archived files will come in the next pr in the stack)
- confirmation modals should prompt to confirm to proceed to restoring
or permanently deleting a noncurrent version
https://github.com/user-attachments/assets/803a6f15-7f32-44ae-9e24-1c201aab178a
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Problem
The deprecated indicator next to extensions like `pgjwt` on Database →
Extensions was a `ButtonTooltip` styled to look like a badge
(`variant="warning"`, triangle icon, `rounded-full`). That adds another
one-off badge-like control instead of using the shared `Badge`.
## Solution
Replace it with a warning `Badge` plus the same tooltip content (when
the extension will be removed).
| Before | After |
| --- | --- |
| <img width="356" height="114" alt="CleanShot 2026-10-09 at 10 38
08@2x"
src="https://github.com/user-attachments/assets/98a2356e-203e-4946-b399-63a53ea9804f"
/> | <img width="312" height="116" alt="CleanShot 2026-10-09 at 10 43
08@2x"
src="https://github.com/user-attachments/assets/ee9ff91d-79f6-4a50-b508-ebe602497cfe"
/> |
## Review instructions
1. Open Studio → Database → Extensions.
2. Find `pgjwt` (or another deprecated extension).
3. Confirm a warning `Deprecated` badge appears next to the name (no
triangle icon / pill button).
4. Hover (or focus) the badge and confirm the removal tooltip still
shows.
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## Problem
On tables with a sticky last column (Database Extensions, OAuth Apps,
Custom Auth Providers), hovering a row lets scrolled cell content show
through the sticky column.
Root cause: row hover uses `bg-surface-200`, which maps to `--muted`
(alpha). That works for normal cells (nothing behind them) but sticky
cells sit above horizontally scrolled content, so the tint is
see-through.
## Solution
In `ShadowScrollArea` when `stickyLastColumn` is set:
- Keep an opaque `bg-surface-100` base on sticky cells
- Composite the muted hover tint via `background-image` so hover matches
`TableRow` without becoming transparent
Also:
- Extensions: use `Table containerProps={{ stickyLastColumn: true }}`
instead of nesting a second `ShadowScrollArea` (same pattern as OAuth /
Custom Auth)
- OAuth Apps and Custom Auth Providers: drop redundant per-cell
`bg-surface-100` / `hover:bg-surface-200` so sticky hover comes only
from `ShadowScrollArea`
| Before | After |
| --- | --- |
| <img width="1042" height="98" alt="27097"
src="https://github.com/user-attachments/assets/204a7dc4-d5ac-40b7-87fb-fdb03982eaad"
/> | <img width="1084" height="101" alt="94456"
src="https://github.com/user-attachments/assets/c6db3607-6bbe-4488-a0a5-81f5a0436d66"
/> |
## Review instructions
1. Open Database Extensions. Narrow the viewport or scroll the table
horizontally so the Enabled column is sticky.
2. Hover a row while Links or description content sits under the Enabled
column. The sticky cell should stay opaque and match the row hover tint
(light and dark).
3. Optionally repeat on Auth → OAuth Apps and Auth → Custom Providers
(actions column).
Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
## Context
We've had a number of users run into issues after installing database
extensions in schemas outside of the `extensions` schema - in particular
for the `public` schema, which issues that normally surface are either:
- Inability to enable RLS because the extensions are owned by
`supabase_admin` when created
- Security advisor hence constantly flags this as an issue
- Inability to move the extension for the same reason
- There is a solution for but it requires a lengthy resolution as
outlined here in [our
docs](https://supabase.com/docs/guides/database/extensions/postgis#moving-postgis-to-a-different-schema)
While the option to select a schema to enable the database extension was
for convenience, this is typically more for power users which seems to
be more of a footgun for most other users.
Hence we're opting to remove the schema selection option in the enable
extension modal to also streamline the UX for enabling an extension by
removing that additional cognitive load.
## Changes involved
### Before:
<img width="430" height="248" alt="image"
src="https://github.com/user-attachments/assets/e99ce86c-b56c-4149-86ca-2457da65b179"
/>
### After:
<img width="438" height="328" alt="image"
src="https://github.com/user-attachments/assets/6be599d5-185b-4efe-9718-b50ac0ed4948"
/>
Clicking on the foot note expands a note as such with an alternative for
power users:
<img width="425" height="434" alt="image"
src="https://github.com/user-attachments/assets/7cbf0b59-0ddc-4933-8c6b-4e30a32115b5"
/>
Extensions will be installed in the `extensions` by default unless
otherwise specified by the extension itself within
`default_version_schema`
## Problem
The redesigned pipeline creation flow (mega branch being worked on in
#49243) needs an incremental rollout while the existing creation sheet
remains the default.
## Solution
- Register **Pipeline creation wizard** in Feature Previews, with
visibility controlled by the `pipelineCreationWizard` ConfigCat flag.
- Require the flag and an explicit user opt-in (via Feature previews
dialog) before enabling the preview.
This adds the preview registration, copy, storage key, and gate. It does
not change creation routing or ship the wizard. The complete gated flow
can be tested on #49243.
The ConfigCat flag is configured as follows:
- **Local/staging:** true for everyone.
- **Production:** false by default, true for the **Supabase Team Email**
segment.
The flag makes the preview visible; it does not opt anyone in. Users
enable it once in Feature Previews. No dev toolbar override is needed in
local/staging.
## Review instructions
1. In local or staging hosted Studio, open **Feature previews**. Find
**Pipeline creation wizard**. With no saved preference, it should be off
by default.
2. Toggle it on and reload. The opt-in should persist. Toggle it off and
reload to confirm opt-out persists. **Add pipeline** still opens the
sheet in this foundation PR.
## Problem
The Pipelines header links to an older feedback discussion. The
dashboard's existing Feedback control already captures the current
route.
## Solution
Remove the “Leave feedback” button and its unused URL constant.
| Before | After |
| --- | --- |
| <img width="1932" height="340" alt="32431"
src="https://github.com/user-attachments/assets/ef5f919c-503b-4fcc-9137-90ddeb6c07dd"
/> | <img width="1930" height="350" alt="CleanShot 2026-10-08 at 15 47
38@2x"
src="https://github.com/user-attachments/assets/d44df2a0-8681-4594-9679-148d639d08fd"
/> |
## Review instructions
1. Open a project's Database > Pipelines page at
`/project/<ref>/database/pipelines`.
2. Confirm the header shows the documentation button without “Leave
feedback”.
3. Confirm the diagram and the usage and enable/disable dropdown actions
are unchanged.
## What kind of change does this PR introduce?
Feature, limited to staging and local.
## What is the current behavior?
On High Availability projects, Studio always disables Realtime: the
Realtime pages and the Publications pages show an "unavailable" message,
the table editor's "Enable Realtime" checkbox is disabled, and the
project home shows Realtime as disabled.
## What is the new behavior?
A new `useIsRealtimeUnavailable()` hook treats Realtime as unavailable
only on High Availability projects outside staging and local (via
`IS_STAGING_OR_LOCAL`). Those four places use it instead of checking
`high_availability` directly.
- **Production:** unchanged.
- **Staging and local:** Realtime behaves as on standard projects.
## Additional context
Whether a High Availability project is actually registered as a Realtime
tenant is gated separately on the platform side. Until then, the pages
render but Realtime connections for that project fail.
Tests: a new `resolveRealtimeUnavailable` test, and the
`TableRealtimeToggle` and `resolveRealtimeServiceStatus` tests updated
for the renamed parameter.
The close button on the Auth users detail panel (and the queue message
panel) is absolutely positioned, but `TabsList` is `position: relative`
and comes later in tree order — so it painted on top of the button and
swallowed every click. The button looked fine and did nothing.
Stacks the button above the tab list, adds an accessible name to the
users panel button, and covers it with an E2E test (Playwright does real
hit-testing, so it fails without the fix).
Fixes FE-4519
## Problem
The log drain form still includes Postgres, ClickHouse, and BigQuery
placeholder variants even though the dashboard does not offer these
destinations. Production Platform API create/update types now exclude
them, causing Studio type errors when the API declarations are
regenerated.
## Fix
Remove the unused variants from the form and submission schemas. Accept
the broader response type for incoming defaults, resolve defaults
through the selectable destination list, and use the form schema type
for the selector.
## How to test
- Run `pnpm --filter studio typecheck` with committed API types and with
types generated from production. Both should pass.
- Run Prettier and ESLint on `LogDrainDestinationSheetForm.tsx`.
- Open project or organization audit log drain settings and add a
supported destination. Available destinations remain unchanged.
Validation: Studio typecheck passes with both committed API types and
freshly generated production API types. Prettier passes; ESLint reports
only existing warnings. Generated declarations are not included in this
PR.
## Context
Adds an empty state for the branch management page, if no preview
branches have yet to be created (overview wont be shown) - mainly visual
changes here. The main intention here is to surface the GH connection
setup a bit more (otherwise the only CTA for that is in the side nav
which is easily missed + its not clear up front what the benefit of the
GH connection is in the context of branching)
This is how it looked like before for reference:
<img width="1346" height="956" alt="image"
src="https://github.com/user-attachments/assets/776ce3f4-e12b-42e4-8e90-0d5ca15dc58f"
/>
And this is what I'm thinking for the empty state:
<img width="1037" height="431" alt="image"
src="https://github.com/user-attachments/assets/d3a8871e-74d2-499a-b317-f739ed925668"
/>
GH connection will flip its badge and hide the CTA if there already is a
GH connection
<img width="1038" height="443" alt="image"
src="https://github.com/user-attachments/assets/4bf2d34d-6c74-4206-b83c-38ba84b99fb3"
/>
## Problem
AlertError forces a vertical layout whenever additional actions are
supplied, even when the caller explicitly requests horizontal or
responsive layout. Stripe Sync Engine's uninstall error is one affected
call site, the other was Pipelines as demoed in #51311.
## Solution
Honour an explicit layout. Preserve the existing defaults: vertical with
additional actions, responsive otherwise.
Replace the standalone Contact support action with an InlineLink in the
contact support prose, preserving support form context and breadcrumb
capture. Keep custom actions such as Retry. If instructions are hidden
or custom prose omits contact support, retain a separate inline support
link. With `hideContactSupport`, show no support link and shorten the
default instructions to “Try refreshing your browser.” Custom
descriptions remain unchanged. The local Pipelines configuration error
explicitly hides support. Add the explicit responsive layout at the
Pipelines call site.
| Before | After |
| --- | --- |
| <img width="914" height="426" alt="CleanShot 2026-10-07 at 17 51
20@2x"
src="https://github.com/user-attachments/assets/6cbddfee-97f5-4a7a-bafa-7b5dfd6ab8bf"
/> | <img width="916" height="422" alt="CleanShot 2026-10-07 at 18 26
06@2x"
src="https://github.com/user-attachments/assets/a21952a6-1c1a-437c-af73-c5736f33fd98"
/> |
| <img width="1566" height="384" alt="CleanShot 2026-10-07 at 18 28
07@2x"
src="https://github.com/user-attachments/assets/bdc2a043-421f-4c22-9bd3-37859c6e85c7"
/> | <img width="1568" height="308" alt="CleanShot 2026-10-07 at 18 26
58@2x"
src="https://github.com/user-attachments/assets/da0f2c6a-6254-4196-944b-5665e87b3c3c"
/> |
## Review instructions
1. In a fresh local test project with no existing `stripe` schema, run
this in SQL Editor:
```sql
begin;
create schema stripe;
comment on schema stripe is
'{"status":"uninstall error","errorMessage":"Local layout test: uninstallation failed"}';
commit;
```
2. Open **Integrations → Stripe Sync Engine → Overview** and reload.
Check **Failed to uninstall Stripe Sync Engine** at wide and narrow
widths, including **Retry uninstallation** and the inline **contact
support** link. Do not click Retry: it invokes the real uninstall
operation.
3. Remove the empty fixture with `drop schema stripe restrict;`.
4. Block the Pipelines source-status request and resize the page: Retry
uses the responsive layout.
5. AlertError callers without an explicit layout should retain their
existing presentation. Confirm default and custom contact support prose
use an inline link, with no standalone support action. Hidden
instructions and custom prose without contact support retain an inline
fallback. With `hideContactSupport`, the default prose is “Try
refreshing your browser.” and no support link appears; custom
descriptions remain unchanged.
6. Automated regression coverage checks that an explicit responsive
layout survives additional actions, and that hiding support removes the
default support wording while preserving custom descriptions.
---------
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
## Context
Resolves https://github.com/supabase/supabase/issues/51330
Odd bug on the Table Editor where "Copy as SQL" CTA would misalign
column names to values if the columns had numerical like names such as
`2024` for example.
Also added an unrelated fix for "Copy as JSON":
- Was adding an `idx` column to the output even if the table didn't have
(was a react data grid internal detail)
- Column name ordering didn't match the table
## To reproduce:
1. Create and populate a table:
```
create table public.yearly_totals (id bigint, "2024" bigint, "2023" bigint);
insert into public.yearly_totals values (7, 99, 42);
```
2. Select the row in the Table Editor, then "Copy as SQL" -> The output
will turn out to be
```INSERT INTO public.yearly_totals (id, "2024", "2023") VALUES (42, 99, 7);```
instead of
```INSERT INTO public.yearly_totals (id, "2024", "2023") VALUES (7,
99,42);```
## To test
- [ ] Verify that the Copy to SQL output matches the intended as per the
set up above
## Problem
Add pipeline opens the creation sheet before users enable Pipelines. A
pending or failed source lookup also lets creation open with an unknown
enablement state.
## Solution
Show the existing enablement dialog first when required, then open
creation after successful enablement. Cancellation and failed enablement
keep creation closed; enabling through the page menu does not open
creation.
Disable both Add pipeline buttons and their keyboard shortcut until the
source lookup succeeds. Failed lookups show an error with Retry,
including the local replication configuration message. Analytics Bucket
keeps its existing creation path.
| Before | After |
| --- | --- |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/cf328732-4342-4758-bde2-98b393341d6a"
/> | _No longer in sheet; dialog is shown conditionally before sheet._ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/f593e271-d29c-4955-8849-0bb64946d4cc"
/> | <img width="1275" height="919" alt="Pipelines Database Shears
Toolshed Supabase"
src="https://github.com/user-attachments/assets/3257d391-b892-44df-85d1-5a2108072312"
/>|
| _“Enable Pipelines”_ | _“Enable”_ |
| After |
| --- |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/b11770a1-4785-49ee-950d-d821892b3245"
/> |
| _Loading_ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/710f82be-ca6f-4337-a5ce-b65f9d7f6a32"
/> |
| _Lookup failed_ |
| <img width="1275" height="919" alt="Pipelines Database Shears Toolshed
Supabase"
src="https://github.com/user-attachments/assets/68486ca8-37e8-4b27-89ae-8004f159c38a"
/> |
| _Plan-access loading, throttled_ |
## Review instructions
Use a project with Pipelines access and a working replication API (which
should work on [deploy
preview](https://studio-staging-git-dnywh-fixpipeline-enable-create-supabase.vercel.app/)).
Test the PR preview or locally
([instructions](https://app.notion.com/p/supabase/Danny-s-Local-ETL-Pipelines-Setup-3b25004b775f8058a108f8f67fc813e9?source=copy_link)).
In DevTools Network, enable **Disable cache** before each reload.
Analytics Bucket intentionally bypasses the source-status guard.
1. **Loading:** select **Slow 3G**, reload, and watch the request ending
in `/replication/<ref>/sources`. While it is pending, both **Add
pipeline** buttons must be disabled and **Shift+N** must open nothing.
Both buttons replace the plus with a loading spinner and have no loading
tooltip. Restore **No throttling** afterwards.
2. **Lookup failed:** right-click that source request and choose **Block
request URL**, then reload. After retries finish, expect **Failed to
retrieve pipeline enablement status** with **Retry**, disabled Add
buttons whose tooltip matches the error title, and no sheet/dialog from
**Shift+N**. An unconfigured local replication API instead shows
**Replication unavailable locally**. Unblock the request before clicking
**Retry**.
3. **Lookup succeeded:** on a disposable project with Pipelines
disabled, successful Retry restores Add pipeline. Clicking it opens
**Enable Pipelines**. Cancel stays on the list; **Enable** opens the
sheet after successful enablement. On an already enabled project, Add
pipeline opens the sheet directly. Enabling through the page's three-dot
menu stays on the list. Analytics Bucket opens its sheet without ETL
enablement.
4. **Plan-access loading is separate:** in Chrome 145 or newer, find
`/organizations/<slug>/entitlements` in Network, right-click it and
choose **Throttle request**. In the **Request conditions** drawer,
select Slow 3G for that request only, leaving global throttling off.
Reload on a Pro organisation with Pipelines disabled and open Add
pipeline after the source lookup succeeds. While entitlements remain
pending, expect body shimmers, the accessible “Checking Pipelines
access…” status, a disabled loading **Enable** button, and no upgrade
prompt. Remove the request condition afterwards. If your DevTools lacks
per-request throttling, use the component tests for deterministic
coverage.
## Problem
The project overview shows the project's compute size in a card labeled
"Compute", but the card is not clickable. Compute changes are managed on
Infrastructure settings, so users have to find that page on their own.
The adjacent cards (GitHub, Recent branch, Last migration, Last backup)
already link to where you manage them.
## Solution
- Rename the card label from "Compute" to "Machine size" and pass
`href={getInfrastructurePath(ref)}` to `SingleStat`. That renders the
same `Link` wrapper as the adjacent cards, so it gets the same link
semantics, keyboard focus and hover style. The value (compute badge or
"Unknown", plus the High Availability badge) is unchanged.
- Add `onClick={(e) => e.stopPropagation()}` to the
`HighAvailabilityBadge` hover card content. This is the same pattern
`ComputeBadgeWrapper` already uses. The hover card is portaled, but
React still bubbles its clicks to the new card link. Without this, the
link's `onClick` would intercept clicks inside the HA hover card.
Clicking "Read more" would then go to Infrastructure instead of opening
the docs in a new tab.
Out of scope: "Compute" labels elsewhere, the Infrastructure page and
compute provisioning behavior.
Notes:
- The compute badge's hover card trigger already calls `stopPropagation`
on click, because it also sits inside clickable project cards and table
rows. When you click the badge itself, the browser still follows the
native anchor to Infrastructure settings, but it does a full page load
instead of a client-side navigation. This PR leaves that shared
component unchanged.
- While the project is resizing, the overview is replaced by the
resizing state, so the card is not shown then. Settings routes stay
reachable while a project is building, so the link doesn't bounce users
back home.
- No new component test: rendering `ActivityStats` needs mocks for about
eight queries, including `ServiceStatus`. The change only sets an `href`
on an existing component, so a browser check is a better fit.
## Review instructions
1. Open a platform project's overview (`/project/<ref>`).
2. The card next to Status reads "Machine size" and still shows the
compute badge (and the HA badge on HA projects).
3. Click the card, or Tab to it and press Enter. You land on
`/project/<ref>/settings/infrastructure`.
4. Hover the compute badge: its hover card still opens, and "Upgrade
compute" still goes to Infrastructure.
5. On an HA project, hover the High Availability badge and click "Read
more". The docs open in a new tab and the page does not navigate.
6. GitHub, Recent branch, Last migration and Last backup keep their
labels and destinations.
Checks: `tsc --noEmit` for Studio (no new errors; one unrelated error in
`packages/ui-patterns/.../InstructionBlocks.tsx` was already there),
`eslint` on the touched files (no new warnings), Prettier check, `vitest
components/interfaces/ProjectHome` (34 passed).
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
---------
Co-authored-by: supabase-vercel-tedd[bot] <336548405+supabase-vercel-tedd[bot]@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` ◀ | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |
## [4/10] Storage object versioning: object versions data layer
**Base:** `feat/storage-versioning/003-bucket-modals` (PR 3)
### This PR
The query and mutation hooks for the version history UI, written to
`queryOptions` using the real Storage endpoints.
- `object-versions-query.ts` — the version list, plus `ObjectVersion`
and `LifecyclePolicy`
- `object-version-restore-mutation.ts` — promote a noncurrent version to
current
- `object-version-delete-mutation.ts` — remove one specific version
- `object-purge-mutation.ts` — delete an object and every version,
bypassing versioning
- `VersionHistory.utils.ts` — `computeVersionFate`, the pure rule
deciding what removal outlook each version row shows
- `BroomSparklesIcon.tsx` — inline SVG for a glyph absent from
lucide-react 0.436
Easier to test directly from next PR in the stack #49208 which wires the
queries to the real file preview panel ui.
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` ◀ | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |
## [3/10] Storage object versioning: wire into the bucket modals
**Base:** `feat/storage-versioning/002-bucket-form-fields` (PR 2)
### This PR
Mounts the object-versioning form section in the create and edit bucket
modals behind the feature preview, and saves it.
- create and edit bucket modals spread `bucketVersioningFormFields` into
their existing form schema
- lifecycle defaults to 30 days / 10 versions
- edit adds a confirmation before suspending an actively versioned
bucket
## Enabling object versioning on a new bucket and setting lifecycle
policies
https://github.com/user-attachments/assets/194f8319-4929-432e-8a50-206f180a77a8
## Edit and suspend object-versioning
https://github.com/user-attachments/assets/f31e1d34-9840-4f5a-a269-6a911214742d
## To reproduce
1. Make sure storage versioning is enabled under feature previews >
Storage Versioning
2. Open Storage Bucket File explorer
3. create new bucket and enable Object Versioning
4. set lifecycle policy
- Noncurrent version expiration: can be either empty or >1
- Retained noncurrent versions: can be either empty or between 1 and 100
and can't exist without "Noncurrent version expiration"
5. Open new bucket with object versioning and test changing lifecycle
policies
6. Disabling object-versioning shows proper warning and updates
`versioning_status` to SUSPENDED (it can never go back to DISABLED once
it has been enabled on a bucket)
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
## Problem
`parseCronJobCommand` reads the timeout of an `net.http_get` /
`net.http_post` cron command like this:
```ts
const timeout = timeoutMatch?.[1] || ''
// ...
timeoutMs: Number(timeout ?? 1000),
```
When the command has no `timeout_milliseconds` argument, `timeout` is
`''`, which isn't nullish, so the `?? 1000` fallback never applies and
`timeoutMs` becomes `Number('') === 0`.
`timeout_milliseconds` is optional in pg_net, so this is common for jobs
created in SQL, e.g.:
```sql
select cron.schedule('ping', '* * * * *', $$ select net.http_get(url:='https://example.com/health') $$);
```
Opening such a job in the cron editor shows a timeout of **0 ms**, and
saving fails validation (the field requires 1000 to 5000 ms) until the
user edits a value they never set.
## Solution
Use the parsed value when present, and otherwise fall back to pg_net's
default for `timeout_milliseconds` (5000 ms in current pg_net). That's
what the job actually runs with, so opening and saving it in Studio
doesn't change its behavior. It's also within the form's allowed range.
Added tests for the Edge Function and HTTP request paths without
`timeout_milliseconds`. Both fail on `master` (`timeoutMs: 0`), and the
existing cron tests still pass.
## Review instructions
1. In the SQL editor, run:
```sql
select cron.schedule('ping', '0 * * * *', $$ select
net.http_get(url:='https://example.com') $$);
```
2. Open Integrations > Cron > `ping` > Edit.
3. Before: the timeout field shows `0` and saving shows a validation
error. After: it shows `5000` and saves.
4. `pnpm --filter studio test
components/interfaces/Integrations/CronJobs/CronJobs.utils.test.ts`
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
## Problem
We want to be able to show free-plan organizations a simplified region
selector that only lists general regions (Americas, Europe,
Asia-Pacific), controlled per organization through ConfigCat.
## Solution
- ConfigCat flags are now evaluated with `organization_slug` and
`organization_created_at` (Unix seconds) custom attributes, so flags can
target and bucket by organization.
- `organization_created_at` is read from `GET
/platform/organizations/{slug}`, fetched only for free-plan
organizations, since the organization list response doesn't include it.
- Two flags:
- `freeTierGeneralRegionEnrollment`: the organization is enrolled
(control or test).
- `freeTierGeneralRegionSelection`: the organization sees only general
regions.
- For enrolled free organizations, the region selector stays in its
loading state until flags have been evaluated with the organization's
creation time, so specific regions aren't shown and then removed.
- In the test variant, the selector hides specific regions and shows a
footer linking to the plan upgrade panel. High Availability keeps its
own region list.
- Telemetry: new `free_tier_general_region_experiment_exposed` and
`free_tier_general_region_upgrade_clicked` events, and
`freeTierGeneralRegionExperiment` / `regionSelectionType` properties on
`project_creation_simple_version_submitted`.
- `created_at` is added to `OrganizationSlugResponse` in the generated
platform types, matching the API.
## Review instructions
1. With both flags off, open `/new/[slug]` for a free organization and
confirm the region selector is unchanged.
2. Using the dev toolbar, set `freeTierGeneralRegionEnrollment` and
`freeTierGeneralRegionSelection` to `true`. Confirm only general regions
are listed and the footer links to the billing plan panel.
3. Set `freeTierGeneralRegionSelection` to `false` and confirm the full
selector is shown.
4. Repeat with a paid organization and confirm the full selector is
always shown.
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
The scoped OAuth consent interstitial: scope display, org/project
selection,
consent screen assembly, success screen, and post-submit role-validation
error state. Consolidates the former stacked PRs #49481, #49484, #49486,
#49489, #49951 into one reviewable unit (each contained the last; no
code
was dropped).
- Entirely behind `useFlag('oauthAppScopedGrants')` — no reachable UI
with
the flag off.
- Runs on the mock data layer from #49476 (`USE_MOCKS`-gated); no real
endpoints are called.
- Covers PROD-626, PROD-627, PROD-628, PROD-629, PROD-653.
Base is #49476 (data contracts + mocks); will retarget to master once it
merges.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
## Problem
Auth Providers uses a bespoke Enabled/Disabled chip that mixes brand
tokens with primary colour. Custom Auth Providers already uses the
standard `Badge` for the same meaning.
## Solution
Replace the bespoke chip with the existing `Badge`:
```tsx
<Badge variant={isActive ? 'success' : 'default'}>
{isActive ? 'Enabled' : 'Disabled'}
</Badge>
```
No new status primitive in this PR. Built-in providers now match Custom
Providers.
| Before | After |
| --- | --- |
| <img width="958" height="1568" alt="CleanShot 2026-10-06 at 17 19
40@2x"
src="https://github.com/user-attachments/assets/08850f4e-528a-4e47-958f-207e72783b16"
/> | <img width="934" height="1482" alt="CleanShot 2026-10-07 at 11 46
41@2x"
src="https://github.com/user-attachments/assets/232903f7-86bd-4c5c-ae1d-d873cddcf67b"
/> |
## Review instructions
1. Open Auth → Providers (built-in list). Confirm each provider shows a
small `Enabled` / `Disabled` Badge, with no brand/primary token mash-up
or oversized pill.
2. Optional: open Auth → Providers → Custom and compare the Enabled
column; it should feel like the same Badge treatment.
## Checklist
- [x] I have read
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
- [ ] If I wrote a new docs topic or edited an existing topic, I used
the `/write-the-docs` or `/edit-the-docs` skill, which applies the docs
[style
guide](https://github.com/supabase/supabase/tree/master/apps/docs/style-guide)
## Problem
Dashboard users need a notice about the Terms of Service update
alongside #51106 and #51107.
## Solution
Reuse the organisation landing-page notice pattern from #50397. A
compact “We've updated our Terms of Service.” notice opens the
explanation and agreement link through **Learn more**. Closing the
notice or choosing **Got it** remembers dismissal in the browser with a
new version-specific key.
```text
Organisation landing page
Notice → Learn more → Explanation and Terms of Service link
Close / Got it → Remember dismissal
```
| After |
| --- |
| <img width="628" height="444" alt="CleanShot 2026-10-06 at 14 47
46@2x"
src="https://github.com/user-attachments/assets/72922712-321f-4972-b20c-1a075e0745d0"
/> |
| _Banner_ |
| <img width="1078" height="718" alt="CleanShot 2026-10-06 at 16 58
18@2x"
src="https://github.com/user-attachments/assets/2109c2ab-e9b7-4855-8acb-42d5232cd002"
/> |
| _Dialog_ |
## Review instructions
1. Open `/organizations` or an organisation's `/org/<slug>` landing page
in the hosted Studio preview. Expect the compact notice.
2. Click **Learn more**. Expect the explanation and a link to the Terms
of Service. Escape closes the dialog without dismissing the notice.
3. Choose **Got it**, then reload. The notice stays dismissed. Repeat in
a fresh browser profile using **Close banner**.
4. Open a project or an organisation settings page. The notice should
not appear.
## Summary
* Makes sign-in banner visible on unauthenticated pages
* Show only when one of the affected components is Dashboard
* Works only when new status page (under feature flag) is being used
* Regression fix: Auth pages without the status banner (sign-up,
sign-in-mfa, sign-in-recovery-code, sign-in-sso, sign-in-partner,
forgot-password, forgot-password-mfa, reset-password) no longer reserve
dead space at the top for a banner they don't render
## Test plan
- [X] TypeScript: `tsc --noEmit` passes clean, no type errors
- [X] Unit tests: All new/existing tests pass in
`apps/studio/lib/status-page/status-page.utils.test.ts` and
`apps/studio/components/layouts/AppLayout/StatusBanner.utils.test.ts`
(697 files / 7833 tests, no regressions)
- [X] Manual verification: Sign-in page still renders the banner
correctly when an incident affecting Dashboard is active; no layout
regression
**Related:**
[FE-3443](https://linear.app/supabase/issue/FE-3443/only-show-sign-in-incident-banner-when-relevant)
## Summary
Removes the "Resizes may require more downtime than normal on this
project." warning from the disk/compute review dialog. This warning was
added for cross-architecture migrations during resize operations, which
are no longer performed, and has caused unnecessary customer support
inquiries.
## Closes
FE-4543
https://linear.app/supabase/issue/FE-4543
## Problem
The Studio unit test added in #51112 failed on master because the
`/after` element returned by `findByText` was detached by a rerender
before `toBeInTheDocument` ran. The failure repeated across all three
test attempts in [this
job](https://github.com/supabase/supabase/actions/runs/37480673016/job/112327649118).
## Fix
Retry the DOM lookup and assertion together with `waitFor`, so each
attempt checks the current element synchronously. Keep the existing
timeout and assertions covering pathname refresh before the URL updates.
## How to test
- Run `pnpm --filter studio exec vitest run
components/interfaces/UnifiedLogs/UnifiedLogs.test.tsx`.
- Expected result: pathname options refresh after selecting POST and the
test passes.
- Prettier and `git diff --check` pass. Local Vitest was blocked before
assertions by the available dependency tree using React 18 instead of
React 19; ESLint was blocked by a missing `@eslint/compat` dependency.
CI validation is pending.
## Problem
Unified Logs included a pathname aggregation in every initial sidebar
count query, even when the pathname filter was closed. This issue is
tracked in
[DEBUG-230](https://linear.app/supabase/issue/DEBUG-230/fetch-sidebar-counts-only-when-needed).
## Fix
Remove pathname aggregation from the initial ClickHouse and BigQuery
count queries while keeping the existing shared count scans. Fetch
scoped pathname options through the existing facet query when the filter
opens or its search changes. Order limited pathname results by count,
validate response rows with Zod, and retain selected paths during
loading and validation errors. Use live sidebar filters while their URL
update is pending and URL filters after navigation. Cache results by
project and filter scope, and wait for feature flags before requesting
options.
## How to test
- Open Unified Logs, then open Pathname and search. Confirm options load
on demand.
- Change the time range, another filter, or navigate through browser
history. Confirm the options reflect the current scope.
- Close and reopen Pathname without changing the scope. Confirm cached
options return.
- Run the pathname filter component tests and Studio typecheck.
## Context
Similar to domain to https://github.com/supabase/supabase/pull/51256 -
`getTableDefinitionSql` doesn't escape SQL identifiers, which generates
invalid SQL on the dashboard's table editor for the "Copy table schema"
CTA, or the table definition tab.
Also fixes the "Copy table schema" CTA which was missing the `scoped`
parameter when calling `getTableDefinition`
Changes here addresses this issue, can test with a table named like
`test"table`
## Context
Adds virtualization to the organization team members page - browser
performance was facing issues for organizations with a large amount of
members (e.g 1000+), primarily due to some computation within
`MemberActions.tsx`, so virtualization addresses this by controlling the
number of member rows being rendered in the DOM at any one time.
<img width="1182" height="435" alt="image"
src="https://github.com/user-attachments/assets/e3da7036-c1c8-4d73-a363-85ecbdb79179"
/>
## Unrelated changes
- Updated `TeamSettings` to use the `PageContainer` components for UI
consistency
- Updated user `ProfileImage` to render the first alphabet of the email,
rather than a generic user icon
<img width="275" height="126" alt="image"
src="https://github.com/user-attachments/assets/17eae3b1-c527-4b8f-afcd-c5151bdaf869"
/>
- Updated row heights of member rows to be more smaller
- Updated MFA column to use tooltips with a clearer CTA for members that
don't have MFA enabled
<img width="332" height="144" alt="image"
src="https://github.com/user-attachments/assets/a479af31-7fec-454d-b64e-e6314fd6d55e"
/>
- Added a filter for MFA status
<img width="375" height="177" alt="image"
src="https://github.com/user-attachments/assets/fd87105e-524d-4ded-b471-769592be96c7"
/>
## To test
- Can override the content for the `members` network request with the
following sample JSON, main thing is just to test that initial load +
searching should not run into any significant browser performance
issues.
[members-response-1000.json](https://github.com/user-attachments/files/33100317/members-response-1000.json)
- Can also test on production that this mock response does indeed cause
browser performance issues as well
| # | Branch | Base |
| - | ------ | ---- |
| 1 | `feat/storage-versioning-private-alpha` — merged | `master` |
| 2 | `feat/storage-versioning/002-bucket-form-fields` ◀ | `master` |
| 3 | `feat/storage-versioning/003-bucket-modals` | 2 |
| 4 | `feat/storage-versioning/004-object-versions-data` | 3 |
| 5 | `feat/storage-versioning/005-file-preview-versions` | 4 |
| 6 | `feat/storage-versioning/006-billing-storage-retention` | 5 |
| 7 | `feat/storage-versioning/007-archived-objects-data` | 6 |
| 8 | `feat/storage-versioning/008-archived-rows` | 7 |
| 9 | `feat/storage-versioning/009-archived-preview-pane` | 8 |
| 10 | `feat/storage-versioning/010-replace-file` | 9 |
## [2/10] Storage object versioning: bucket form fields
The object versioning + lifecycle policy form section for the create and
edit bucket modals.
Mounted onto the ui in PR 3 #49205
- `BucketVersioningFields` — the versioning switch and the suspension /
public-bucket / retention-tightening warnings
- `LifecyclePolicySection` — the retention window and version cap inputs
- `ExpirationModeToggle` — how the two conditions combine (and / or)
- `BucketVersioningFields.schema.ts` — zod fields the parent modals
spread into their own schema, plus `superRefineBucketVersioning`
- `BucketVersioningFields.utils.ts` — retention-tightening detection
- `StorageVersioning.constants.ts` — versioning state and expiration
mode types, the prefill defaults, and `getBucketVersioningState`
Note: a single s3 lifecycle policy expects both `version_expiry_days`
and `max_noncurrent_versions` and always evaluate the two fields within
the same policy with an AND logic. To enable both AND and OR/EITHER
logic, we save two distinct s3 policies so we can enforce the OR logic.
See demos and how to reproduce in #49205
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary
* **New Features**
* Eligible projects with the preview enabled can configure object
versioning for storage buckets, including version expiration,
retained-version limits, and “and/or” lifecycle conditions.
* Settings default to 30 days and 10 retained versions, with validation
for retention values and requirements for setting a version limit.
* Notices highlight public buckets, missing lifecycle conditions,
suspending existing versioning, and changes that tighten retention
limits.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
## Context
Resolves https://github.com/supabase/supabase/issues/49977
Addresses an issue in `formatTableRowsToSQL` to use `ident` for schema,
table, and column name which will handle escaping of SQL identifiers.
Can verify fix by creating a table like `test"table`, then adding some
rows, and selecting either Copy as SQL or Export as SQL
## Context
Adds a couple of branching nav items to Command K
- Create new branch
- Branch management
- Merge requests
- Github Connection (For branching)
- Branching feedback
Switch branch is still available, and only visible after a branch has
been created (status quo)
<img width="610" height="531" alt="image"
src="https://github.com/user-attachments/assets/3068184e-889d-44ed-8cf6-2afd59ffb109"
/>
## Context
Prevents organizations from enabling spend cap if the org has projects
with read replicas - We currently gate creation of read replicas to
ensure that orgs have spend caps disabled, but were missing the guard
for the other way around
<img width="662" height="378" alt="image"
src="https://github.com/user-attachments/assets/44ad036c-4c1b-48e8-beb7-f16f6170c9fb"
/>
## Other changes involved
- Refactored to use new `Sheet` and `Table` components in
`SpendCapSidePanel`
I removed the Studio Privacy Policy update notice that #50397 added on
2026-09-16, when Privacy Policy v4 took effect. It has been up for
almost three weeks, and the ToS v4 banner (#51109) goes out next. I did
the same in #44380, removing the March 2026 privacy notice after 15
days.
This is the exact inverse of #50397: the banner component and its test,
the banner ID, the dismissal local storage key, and the org-landing path
helper that only this notice used.
## To test
Tested on Vercel preview:
- [ ] In a fresh browser profile (no
`privacy-policy-update-2026-09-16-dismissed` key), open
`/organizations`: expect no Privacy Policy notice
- [ ] Open `/org/<slug>`: expect no Privacy Policy notice and the
project list renders normally
- [ ] Open a project's Logs page: expect the logs deprecation banner
behavior unchanged (only shows before its expiry)
## Linear
- fixes GROWTH-1322
## Problem
The theme's primary hue can change in CSS, but Appearance had no way to
try other spot colours. That makes it hard to find controls whose colour
still depends on the fixed Supabase brand palette.
## Solution
Add a **Spot color** control under Appearance → Theme colors
(employee-only via ConfigCat `appearanceSpotColor`, targeted to Supabase
Team Email).
### Spot color UX
- Rainbow spectrum track with a thin outline so pale tracks stay visible
- Live trifecta swatches for `--primary-solid`, `--primary`, and
`--primary-bright` (darkest → lightest) next to the degree readout
- Drag updates are rAF-batched so React paint and CSS preview stay to
one frame
### Canvas tint coupling
- `--surface-hue` is derived in CSS as `calc(var(--primary-hue) +
var(--surface-hue-offset))`
- Dark: offset `0` (same hue as spot)
- Light: offset `180` (complementary canvas tint; brand green ≈157.5° →
rose ≈337.5°)
- No JS override of `--surface-hue`. Changing Spot color moves primary
controls and the low-chroma canvas tint together
### Other theme sliders
- Renamed **Color intensity** → **Surface tint** (it only drives the
neutral ramp via `--chroma`, not spot chroma)
- Meaning-shaped tracks for every knob (spectrum, grey→tint, soft→hard,
dark→light, flat→lift)
- Same outline treatment on those tracks
| Before | After |
| --- | --- |
| <img width="1476" height="2174" alt="CleanShot 2026-10-05 at 15 02
21@2x"
src="https://github.com/user-attachments/assets/d08b0fa8-32af-450e-adce-861f59c9d6ca"
/> | <img width="1474" height="2354" alt="CleanShot 2026-10-05 at 14 56
35@2x"
src="https://github.com/user-attachments/assets/9a1e6183-a4ba-4c8e-a458-bb0eea946db8"
/> |
| _Anyone else_ | _With staff flag, custom settings_ |
## Review instructions
1. Confirm ConfigCat flag `appearanceSpotColor` is on for your staff
account (or flip it in the Dev Toolbar).
2. Open `/account/me` → **Appearance → Theme colors**.
3. Without the flag: Spot color is hidden; other theme sliders still
work.
4. With the flag: drag Spot color in light and dark. Primary controls
and canvas tint should move together; Supabase brand assets should stay
fixed.
5. Raise Surface tint and confirm the canvas hue follows the
complementary (light) or same-hue (dark) offset.
6. Refresh, switch modes, and use **Reset** to check persistence and
defaults.
The Realtime Inspector now checks the Realtime publication before
showing setup guidance. Projects with published tables get the
join-channel view even before this Inspector session receives any
messages; unconfigured projects keep the setup guide.
Setup guidance also stays hidden while publications are loading or
unavailable. Joining a channel and displaying received messages retain
their existing behavior.
Addresses
[FE-4520](https://linear.app/supabase/issue/FE-4520/realtime-inspector-ui-implies-i-am-not-using-realtime-despite-already).
## To test
- With no tables in `supabase_realtime`, open Realtime → Inspector and
confirm the setup guide appears.
- Enable Realtime on a table and confirm a client receives a database
change. Open the Inspector without joining a channel: it should show
“Join a channel to start listening to messages” and no setup guide.
- Join a channel, trigger a table change, and check the event and
payload appear. Stop listening and confirm messages remain visible.
- Open Policies, then return to the Inspector and confirm the setup
guide stays hidden for the configured project.
- Join a broadcast-only channel with no incoming messages and confirm
the messages view appears immediately.
## Validation
Reproduced the original prompt locally with a working Realtime table,
then verified the fix in the browser, including an independent client
subscription, a live INSERT in the Inspector, navigation, stopping, and
the unconfigured state. Temporary test data and services were cleaned
up.
All nine new regression tests pass; four fail against the original code.
Typecheck, formatting, lint ratchet, Knip, and the case-sensitivity
check pass. The full Studio suite passed 7,799 tests with one unrelated
Explorer test failure; that test passed on a focused rerun alongside the
Inspector tests.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* The Realtime inspector keeps the messages view visible while
publication status is loading or unavailable, and when a channel is
joined or messages are present.
* Setup guidance appears only after publications load successfully and
confirm that Realtime is unavailable, with no channel or messages to
show. This includes cases where there are no publications, the Realtime
publication has no tables, or only a differently named publication
exists.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
## Problem
When building scoped pat's we had omitted the option to have them never
expire.
## Solution
This re-adds the option to select "never" and it comes with the caveat
of an admonition to warn the user that they would need to manually
delete or revoke this token.
## Review instructions
Provide a clear numbered procedure that the PR reviewer can walk
through.
1. Open /account/tokens
2. Click Generate new token.
3. Open Expires in. Confirm "Never" is the last option, after "Custom",
and has no Recommended badge.
4. Select Never. A warning admonition appears directly below the expiry
row: "This token never expires — Anyone with the token keeps access
until you delete it."
5. Pick an org and project, grant one permission, click Review access.
Summary shows Expires: Never.
6. Create the token. The POST body has no expires_at, and the new row's
Expires column reads Never.
Fixes FE-4527.
Co-authored-by: Ali Waseem <waseema393@gmail.com>
## Summary
The "> 8 GB warning" when spend cap enabled used to be conflated with
the "has replicas with spend cap" warning, which causes a confusing
error message.
Opting to split them out into 2 separate warnings to give the user a
better description of the problem.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Read replicas alone no longer trigger the disk-size threshold warning.
* **New Features**
* When usage billing is disabled, a warning appears if read replicas are
present and no project exceeds 8 GB, with guidance on next steps.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
We were using a custom segment control. Recently we introduced segmented
toggle groups in our design system. The old one is inconsistent and
doesn't match anything else.
## Solution
Replace segmented control with [this
one](https://supabase.com/design-system/docs/components/toggle-group#segmented).
| Before | After |
|--------|--------|
| <img width="777" height="85" alt="Screenshot 2026-10-01 at 11 36 47"
src="https://github.com/user-attachments/assets/84e28075-248d-42d4-a537-f18cd2fe86db"
/> | <img width="783" height="95" alt="Screenshot 2026-10-01 at 11 37
00"
src="https://github.com/user-attachments/assets/1071f031-8e96-4db1-8ea1-36cb34e9923a"
/> |
## Test plan
- [ ] Go to Account Settings → Access Tokens, create a new scoped token,
and on the capability review step confirm the All/Read/Read-write
segmented control renders correctly and filters the capability list as
expected
- [ ] Open an existing scoped token's "View" sheet and confirm the same
segmented control filters correctly there too
- [ ] Verify keyboard navigation (arrow keys) and that exactly one
option is always selected (no deselect state)
- [ ] Visual check against the design system's segmented `ToggleGroup`
styling (no leftover custom border/divider artifacts from the old
implementation)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Style**
* Updated the capability-level selector to use a segmented control.
Selection behavior remains unchanged.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->